Copying scripts from 2.4x without 2.5x changes
authorCampbell Barton <ideasman42@gmail.com>
Tue, 29 Sep 2009 15:16:22 +0000 (15:16 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Tue, 29 Sep 2009 15:16:22 +0000 (15:16 +0000)
release/scripts/io/export_3ds.py [new file with mode: 0644]
release/scripts/io/export_fbx.py [new file with mode: 0644]
release/scripts/io/export_obj.py [new file with mode: 0644]
release/scripts/io/export_ply.py [new file with mode: 0644]
release/scripts/io/export_x3d.py [new file with mode: 0644]
release/scripts/io/import_3ds.py [new file with mode: 0644]
release/scripts/io/import_obj.py [new file with mode: 0644]

diff --git a/release/scripts/io/export_3ds.py b/release/scripts/io/export_3ds.py
new file mode 100644 (file)
index 0000000..87680bc
--- /dev/null
@@ -0,0 +1,1019 @@
+#!BPY
+# coding: utf-8
+""" 
+Name: '3D Studio (.3ds)...'
+Blender: 243
+Group: 'Export'
+Tooltip: 'Export to 3DS file format (.3ds).'
+"""
+
+__author__ = ["Campbell Barton", "Bob Holcomb", "Richard Lärkäng", "Damien McGinnes", "Mark Stijnman"]
+__url__ = ("blenderartists.org", "www.blender.org", "www.gametutorials.com", "lib3ds.sourceforge.net/")
+__version__ = "0.90a"
+__bpydoc__ = """\
+
+3ds Exporter
+
+This script Exports a 3ds file.
+
+Exporting is based on 3ds loader from www.gametutorials.com(Thanks DigiBen) and using information
+from the lib3ds project (http://lib3ds.sourceforge.net/) sourcecode.
+"""
+
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# Script copyright (C) Bob Holcomb 
+#
+# 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, MA  02111-1307, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+# --------------------------------------------------------------------------
+
+
+######################################################
+# Importing modules
+######################################################
+
+import Blender
+import bpy
+from BPyMesh import getMeshFromObject
+from BPyObject import getDerivedObjects
+try: 
+    import struct
+except: 
+    struct = None
+
+# So 3ds max can open files, limit names to 12 in length
+# this is verry annoying for filenames!
+name_unique = []
+name_mapping = {}
+def sane_name(name):
+       name_fixed = name_mapping.get(name)
+       if name_fixed != None:
+               return name_fixed
+       
+       if len(name) > 12:
+               new_name = name[:12]
+       else:
+               new_name = name
+       
+       i = 0
+       
+       while new_name in name_unique:
+               new_name = new_name[:-4] + '.%.3d' % i
+               i+=1
+       
+       name_unique.append(new_name)
+       name_mapping[name] = new_name
+       return new_name
+
+######################################################
+# Data Structures
+######################################################
+
+#Some of the chunks that we will export
+#----- Primary Chunk, at the beginning of each file
+PRIMARY= long("0x4D4D",16)
+
+#------ Main Chunks
+OBJECTINFO   =      long("0x3D3D",16);      #This gives the version of the mesh and is found right before the material and object information
+VERSION      =      long("0x0002",16);      #This gives the version of the .3ds file
+KFDATA       =      long("0xB000",16);      #This is the header for all of the key frame info
+
+#------ sub defines of OBJECTINFO
+MATERIAL=45055         #0xAFFF                         // This stored the texture info
+OBJECT=16384           #0x4000                         // This stores the faces, vertices, etc...
+
+#>------ sub defines of MATERIAL
+MATNAME    =      long("0xA000",16);      # This holds the material name
+MATAMBIENT   =      long("0xA010",16);      # Ambient color of the object/material
+MATDIFFUSE   =      long("0xA020",16);      # This holds the color of the object/material
+MATSPECULAR   =      long("0xA030",16);      # SPecular color of the object/material
+MATSHINESS   =      long("0xA040",16);      # ??
+MATMAP       =      long("0xA200",16);      # This is a header for a new material
+MATMAPFILE    =      long("0xA300",16);      # This holds the file name of the texture
+
+RGB1=  long("0x0011",16)
+RGB2=  long("0x0012",16)
+
+#>------ sub defines of OBJECT
+OBJECT_MESH  =      long("0x4100",16);      # This lets us know that we are reading a new object
+OBJECT_LIGHT =      long("0x4600",16);      # This lets un know we are reading a light object
+OBJECT_CAMERA=      long("0x4700",16);      # This lets un know we are reading a camera object
+
+#>------ sub defines of CAMERA
+OBJECT_CAM_RANGES=   long("0x4720",16);      # The camera range values
+
+#>------ sub defines of OBJECT_MESH
+OBJECT_VERTICES =   long("0x4110",16);      # The objects vertices
+OBJECT_FACES    =   long("0x4120",16);      # The objects faces
+OBJECT_MATERIAL =   long("0x4130",16);      # This is found if the object has a material, either texture map or color
+OBJECT_UV       =   long("0x4140",16);      # The UV texture coordinates
+OBJECT_TRANS_MATRIX  =   long("0x4160",16); # The Object Matrix
+
+#>------ sub defines of KFDATA
+KFDATA_KFHDR            = long("0xB00A",16);
+KFDATA_KFSEG            = long("0xB008",16);
+KFDATA_KFCURTIME        = long("0xB009",16);
+KFDATA_OBJECT_NODE_TAG  = long("0xB002",16);
+
+#>------ sub defines of OBJECT_NODE_TAG
+OBJECT_NODE_ID          = long("0xB030",16);
+OBJECT_NODE_HDR         = long("0xB010",16);
+OBJECT_PIVOT            = long("0xB013",16);
+OBJECT_INSTANCE_NAME    = long("0xB011",16);
+POS_TRACK_TAG                  = long("0xB020",16);
+ROT_TRACK_TAG                  = long("0xB021",16);
+SCL_TRACK_TAG                  = long("0xB022",16);
+
+def uv_key(uv):
+       return round(uv.x, 6), round(uv.y, 6)
+
+# size defines:        
+SZ_SHORT = 2
+SZ_INT   = 4
+SZ_FLOAT = 4
+
+class _3ds_short(object):
+       '''Class representing a short (2-byte integer) for a 3ds file.
+       *** This looks like an unsigned short H is unsigned from the struct docs - Cam***'''
+       __slots__ = 'value'
+       def __init__(self, val=0):
+               self.value=val
+       
+       def get_size(self):
+               return SZ_SHORT
+
+       def write(self,file):
+               file.write(struct.pack("<H", self.value))
+               
+       def __str__(self):
+               return str(self.value)
+
+class _3ds_int(object):
+       '''Class representing an int (4-byte integer) for a 3ds file.'''
+       __slots__ = 'value'
+       def __init__(self, val=0):
+               self.value=val
+       
+       def get_size(self):
+               return SZ_INT
+
+       def write(self,file):
+               file.write(struct.pack("<I", self.value))
+       
+       def __str__(self):
+               return str(self.value)
+
+class _3ds_float(object):
+       '''Class representing a 4-byte IEEE floating point number for a 3ds file.'''
+       __slots__ = 'value'
+       def __init__(self, val=0.0):
+               self.value=val
+       
+       def get_size(self):
+               return SZ_FLOAT
+
+       def write(self,file):
+               file.write(struct.pack("<f", self.value))
+       
+       def __str__(self):
+               return str(self.value)
+
+
+class _3ds_string(object):
+       '''Class representing a zero-terminated string for a 3ds file.'''
+       __slots__ = 'value'
+       def __init__(self, val=""):
+               self.value=val
+       
+       def get_size(self):
+               return (len(self.value)+1)
+
+       def write(self,file):
+               binary_format = "<%ds" % (len(self.value)+1)
+               file.write(struct.pack(binary_format, self.value))
+       
+       def __str__(self):
+               return self.value
+
+class _3ds_point_3d(object):
+       '''Class representing a three-dimensional point for a 3ds file.'''
+       __slots__ = 'x','y','z'
+       def __init__(self, point=(0.0,0.0,0.0)):
+               self.x, self.y, self.z = point
+               
+       def get_size(self):
+               return 3*SZ_FLOAT
+
+       def write(self,file):
+               file.write(struct.pack('<3f', self.x, self.y, self.z))
+       
+       def __str__(self):
+               return '(%f, %f, %f)' % (self.x, self.y, self.z)
+               
+# Used for writing a track
+"""
+class _3ds_point_4d(object):
+       '''Class representing a four-dimensional point for a 3ds file, for instance a quaternion.'''
+       __slots__ = 'x','y','z','w'
+       def __init__(self, point=(0.0,0.0,0.0,0.0)):
+               self.x, self.y, self.z, self.w = point  
+       
+       def get_size(self):
+               return 4*SZ_FLOAT
+
+       def write(self,file):
+               data=struct.pack('<4f', self.x, self.y, self.z, self.w)
+               file.write(data)
+
+       def __str__(self):
+               return '(%f, %f, %f, %f)' % (self.x, self.y, self.z, self.w)
+"""
+
+class _3ds_point_uv(object):
+       '''Class representing a UV-coordinate for a 3ds file.'''
+       __slots__ = 'uv'
+       def __init__(self, point=(0.0,0.0)):
+               self.uv = point
+       
+       def __cmp__(self, other):
+               return cmp(self.uv,other.uv)    
+       
+       def get_size(self):
+               return 2*SZ_FLOAT
+       
+       def write(self,file):
+               data=struct.pack('<2f', self.uv[0], self.uv[1])
+               file.write(data)
+       
+       def __str__(self):
+               return '(%g, %g)' % self.uv
+
+class _3ds_rgb_color(object):
+       '''Class representing a (24-bit) rgb color for a 3ds file.'''
+       __slots__ = 'r','g','b'
+       def __init__(self, col=(0,0,0)):
+               self.r, self.g, self.b = col
+       
+       def get_size(self):
+               return 3
+       
+       def write(self,file):
+               file.write( struct.pack('<3c', chr(int(255*self.r)), chr(int(255*self.g)), chr(int(255*self.b)) ) )
+       
+       def __str__(self):
+               return '{%f, %f, %f}' % (self.r, self.g, self.b)
+
+class _3ds_face(object):
+       '''Class representing a face for a 3ds file.'''
+       __slots__ = 'vindex'
+       def __init__(self, vindex):
+               self.vindex = vindex
+       
+       def get_size(self):
+               return 4*SZ_SHORT
+       
+       def write(self,file):
+               # The last zero is only used by 3d studio
+               file.write(struct.pack("<4H", self.vindex[0],self.vindex[1], self.vindex[2], 0))
+       
+       def __str__(self):
+               return '[%d %d %d]' % (self.vindex[0],self.vindex[1], self.vindex[2])
+
+class _3ds_array(object):
+       '''Class representing an array of variables for a 3ds file.
+
+       Consists of a _3ds_short to indicate the number of items, followed by the items themselves.
+       '''
+       __slots__ = 'values', 'size'
+       def __init__(self):
+               self.values=[]
+               self.size=SZ_SHORT
+       
+       # add an item:
+       def add(self,item):
+               self.values.append(item)
+               self.size+=item.get_size()
+       
+       def get_size(self):
+               return self.size
+       
+       def write(self,file):
+               _3ds_short(len(self.values)).write(file)
+               #_3ds_int(len(self.values)).write(file)
+               for value in self.values:
+                       value.write(file)
+       
+       # To not overwhelm the output in a dump, a _3ds_array only
+       # outputs the number of items, not all of the actual items. 
+       def __str__(self):
+               return '(%d items)' % len(self.values)
+
+class _3ds_named_variable(object):
+       '''Convenience class for named variables.'''
+       
+       __slots__ = 'value', 'name'
+       def __init__(self, name, val=None):
+               self.name=name
+               self.value=val
+       
+       def get_size(self):
+               if (self.value==None): 
+                       return 0
+               else:
+                       return self.value.get_size()
+       
+       def write(self, file):
+               if (self.value!=None): 
+                       self.value.write(file)
+       
+       def dump(self,indent):
+               if (self.value!=None):
+                       spaces=""
+                       for i in xrange(indent):
+                               spaces+="  ";
+                       if (self.name!=""):
+                               print spaces, self.name, " = ", self.value
+                       else:
+                               print spaces, "[unnamed]", " = ", self.value
+
+
+#the chunk class
+class _3ds_chunk(object):
+       '''Class representing a chunk in a 3ds file.
+
+       Chunks contain zero or more variables, followed by zero or more subchunks.
+       '''
+       __slots__ = 'ID', 'size', 'variables', 'subchunks'
+       def __init__(self, id=0):
+               self.ID=_3ds_short(id)
+               self.size=_3ds_int(0)
+               self.variables=[]
+               self.subchunks=[]
+       
+       def set_ID(id):
+               self.ID=_3ds_short(id)
+       
+       def add_variable(self, name, var):
+               '''Add a named variable. 
+               
+               The name is mostly for debugging purposes.'''
+               self.variables.append(_3ds_named_variable(name,var))
+       
+       def add_subchunk(self, chunk):
+               '''Add a subchunk.'''
+               self.subchunks.append(chunk)
+
+       def get_size(self):
+               '''Calculate the size of the chunk and return it.
+               
+               The sizes of the variables and subchunks are used to determine this chunk\'s size.'''
+               tmpsize=self.ID.get_size()+self.size.get_size()
+               for variable in self.variables:
+                       tmpsize+=variable.get_size()
+               for subchunk in self.subchunks:
+                       tmpsize+=subchunk.get_size()
+               self.size.value=tmpsize
+               return self.size.value
+
+       def write(self, file):
+               '''Write the chunk to a file.
+               
+               Uses the write function of the variables and the subchunks to do the actual work.'''
+               #write header
+               self.ID.write(file)
+               self.size.write(file)
+               for variable in self.variables:
+                       variable.write(file)
+               for subchunk in self.subchunks:
+                       subchunk.write(file)
+               
+               
+       def dump(self, indent=0):
+               '''Write the chunk to a file.
+               
+               Dump is used for debugging purposes, to dump the contents of a chunk to the standard output. 
+               Uses the dump function of the named variables and the subchunks to do the actual work.'''
+               spaces=""
+               for i in xrange(indent):
+                       spaces+="  ";
+               print spaces, "ID=", hex(self.ID.value), "size=", self.get_size()
+               for variable in self.variables:
+                       variable.dump(indent+1)
+               for subchunk in self.subchunks:
+                       subchunk.dump(indent+1)
+
+
+
+######################################################
+# EXPORT
+######################################################
+
+def get_material_images(material):
+       # blender utility func.
+       images = []
+       if material:
+               for mtex in material.getTextures():
+                       if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE:
+                               image = mtex.tex.image
+                               if image:
+                                       images.append(image) # maye want to include info like diffuse, spec here.
+       return images
+
+def make_material_subchunk(id, color):
+       '''Make a material subchunk.
+       
+       Used for color subchunks, such as diffuse color or ambient color subchunks.'''
+       mat_sub = _3ds_chunk(id)
+       col1 = _3ds_chunk(RGB1)
+       col1.add_variable("color1", _3ds_rgb_color(color));
+       mat_sub.add_subchunk(col1)
+# optional:
+#      col2 = _3ds_chunk(RGB1)
+#      col2.add_variable("color2", _3ds_rgb_color(color));
+#      mat_sub.add_subchunk(col2)
+       return mat_sub
+
+
+def make_material_texture_chunk(id, images):
+       """Make Material Map texture chunk
+       """
+       mat_sub = _3ds_chunk(id)
+       
+       def add_image(img):
+               filename = image.filename.split('\\')[-1].split('/')[-1]
+               mat_sub_file = _3ds_chunk(MATMAPFILE)
+               mat_sub_file.add_variable("mapfile", _3ds_string(sane_name(filename)))
+               mat_sub.add_subchunk(mat_sub_file)
+       
+       for image in images:
+               add_image(image)
+       
+       return mat_sub
+
+def make_material_chunk(material, image):
+       '''Make a material chunk out of a blender material.'''
+       material_chunk = _3ds_chunk(MATERIAL)
+       name = _3ds_chunk(MATNAME)
+       
+       if material:    name_str = material.name
+       else:                   name_str = 'None'
+       if image:       name_str += image.name
+               
+       name.add_variable("name", _3ds_string(sane_name(name_str)))
+       material_chunk.add_subchunk(name)
+       
+       if not material:
+               material_chunk.add_subchunk(make_material_subchunk(MATAMBIENT, (0,0,0) ))
+               material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, (.8, .8, .8) ))
+               material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, (1,1,1) ))
+       
+       else:
+               material_chunk.add_subchunk(make_material_subchunk(MATAMBIENT, [a*material.amb for a in material.rgbCol] ))
+               material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, material.rgbCol))
+               material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, material.specCol))
+               
+               images = get_material_images(material) # can be None
+               if image: images.append(image)
+               
+               if images:
+                       material_chunk.add_subchunk(make_material_texture_chunk(MATMAP, images))
+       
+       return material_chunk
+
+class tri_wrapper(object):
+       '''Class representing a triangle.
+       
+       Used when converting faces to triangles'''
+       
+       __slots__ = 'vertex_index', 'mat', 'image', 'faceuvs', 'offset'
+       def __init__(self, vindex=(0,0,0), mat=None, image=None, faceuvs=None):
+               self.vertex_index= vindex
+               self.mat= mat
+               self.image= image
+               self.faceuvs= faceuvs
+               self.offset= [0, 0, 0] # offset indicies
+
+
+def extract_triangles(mesh):
+       '''Extract triangles from a mesh.
+       
+       If the mesh contains quads, they will be split into triangles.'''
+       tri_list = []
+       do_uv = mesh.faceUV
+       
+       if not do_uv:
+               face_uv = None
+       
+       img = None
+       for face in mesh.faces:
+               f_v = face.v
+               
+               if do_uv:
+                       f_uv = face.uv
+                       img = face.image
+                       if img: img = img.name
+               
+               if len(f_v)==3:
+                       new_tri = tri_wrapper((f_v[0].index, f_v[1].index, f_v[2].index), face.mat, img)
+                       if (do_uv): new_tri.faceuvs= uv_key(f_uv[0]), uv_key(f_uv[1]), uv_key(f_uv[2])
+                       tri_list.append(new_tri)
+               
+               else: #it's a quad
+                       new_tri = tri_wrapper((f_v[0].index, f_v[1].index, f_v[2].index), face.mat, img)
+                       new_tri_2 = tri_wrapper((f_v[0].index, f_v[2].index, f_v[3].index), face.mat, img)
+                       
+                       if (do_uv):
+                               new_tri.faceuvs= uv_key(f_uv[0]), uv_key(f_uv[1]), uv_key(f_uv[2])
+                               new_tri_2.faceuvs= uv_key(f_uv[0]), uv_key(f_uv[2]), uv_key(f_uv[3])
+                       
+                       tri_list.append( new_tri )
+                       tri_list.append( new_tri_2 )
+               
+       return tri_list
+       
+       
+def remove_face_uv(verts, tri_list):
+       '''Remove face UV coordinates from a list of triangles.
+               
+       Since 3ds files only support one pair of uv coordinates for each vertex, face uv coordinates
+       need to be converted to vertex uv coordinates. That means that vertices need to be duplicated when
+       there are multiple uv coordinates per vertex.'''
+       
+       # initialize a list of UniqueLists, one per vertex:
+       #uv_list = [UniqueList() for i in xrange(len(verts))]
+       unique_uvs= [{} for i in xrange(len(verts))]
+       
+       # for each face uv coordinate, add it to the UniqueList of the vertex
+       for tri in tri_list:
+               for i in xrange(3):
+                       # store the index into the UniqueList for future reference:
+                       # offset.append(uv_list[tri.vertex_index[i]].add(_3ds_point_uv(tri.faceuvs[i])))
+                       
+                       context_uv_vert= unique_uvs[tri.vertex_index[i]]
+                       uvkey= tri.faceuvs[i]
+                       
+                       offset_index__uv_3ds = context_uv_vert.get(uvkey)
+                       
+                       if not offset_index__uv_3ds:                            
+                               offset_index__uv_3ds = context_uv_vert[uvkey] = len(context_uv_vert), _3ds_point_uv(uvkey)
+                       
+                       tri.offset[i] = offset_index__uv_3ds[0]
+                       
+                       
+               
+       # At this point, each vertex has a UniqueList containing every uv coordinate that is associated with it
+       # only once.
+       
+       # Now we need to duplicate every vertex as many times as it has uv coordinates and make sure the
+       # faces refer to the new face indices:
+       vert_index = 0
+       vert_array = _3ds_array()
+       uv_array = _3ds_array()
+       index_list = []
+       for i,vert in enumerate(verts):
+               index_list.append(vert_index)
+               
+               pt = _3ds_point_3d(vert.co) # reuse, should be ok
+               uvmap = [None] * len(unique_uvs[i])
+               for ii, uv_3ds in unique_uvs[i].itervalues():
+                       # add a vertex duplicate to the vertex_array for every uv associated with this vertex:
+                       vert_array.add(pt)
+                       # add the uv coordinate to the uv array:
+                       # This for loop does not give uv's ordered by ii, so we create a new map
+                       # and add the uv's later
+                       # uv_array.add(uv_3ds)
+                       uvmap[ii] = uv_3ds
+               
+               # Add the uv's in the correct order
+               for uv_3ds in uvmap:
+                       # add the uv coordinate to the uv array:
+                       uv_array.add(uv_3ds)
+               
+               vert_index += len(unique_uvs[i])
+       
+       # Make sure the triangle vertex indices now refer to the new vertex list:
+       for tri in tri_list:
+               for i in xrange(3):
+                       tri.offset[i]+=index_list[tri.vertex_index[i]]
+               tri.vertex_index= tri.offset
+       
+       return vert_array, uv_array, tri_list
+
+def make_faces_chunk(tri_list, mesh, materialDict):
+       '''Make a chunk for the faces.
+       
+       Also adds subchunks assigning materials to all faces.'''
+       
+       materials = mesh.materials
+       if not materials:
+               mat = None
+       
+       face_chunk = _3ds_chunk(OBJECT_FACES)
+       face_list = _3ds_array()
+       
+       
+       if mesh.faceUV:
+               # Gather materials used in this mesh - mat/image pairs
+               unique_mats = {}
+               for i,tri in enumerate(tri_list):
+                       
+                       face_list.add(_3ds_face(tri.vertex_index))
+                       
+                       if materials:
+                               mat = materials[tri.mat]
+                               if mat: mat = mat.name
+                       
+                       img = tri.image
+                       
+                       try:
+                               context_mat_face_array = unique_mats[mat, img][1]
+                       except:
+                               
+                               if mat: name_str = mat
+                               else:   name_str = 'None'
+                               if img: name_str += img
+                               
+                               context_mat_face_array = _3ds_array()
+                               unique_mats[mat, img] = _3ds_string(sane_name(name_str)), context_mat_face_array
+                               
+                       
+                       context_mat_face_array.add(_3ds_short(i))
+                       # obj_material_faces[tri.mat].add(_3ds_short(i))
+               
+               face_chunk.add_variable("faces", face_list)
+               for mat_name, mat_faces in unique_mats.itervalues():
+                       obj_material_chunk=_3ds_chunk(OBJECT_MATERIAL)
+                       obj_material_chunk.add_variable("name", mat_name)
+                       obj_material_chunk.add_variable("face_list", mat_faces)
+                       face_chunk.add_subchunk(obj_material_chunk)
+                       
+       else:
+               
+               obj_material_faces=[]
+               obj_material_names=[]
+               for m in materials:
+                       if m:
+                               obj_material_names.append(_3ds_string(sane_name(m.name)))
+                               obj_material_faces.append(_3ds_array())
+               n_materials = len(obj_material_names)
+               
+               for i,tri in enumerate(tri_list):
+                       face_list.add(_3ds_face(tri.vertex_index))
+                       if (tri.mat < n_materials):
+                               obj_material_faces[tri.mat].add(_3ds_short(i))
+               
+               face_chunk.add_variable("faces", face_list)
+               for i in xrange(n_materials):
+                       obj_material_chunk=_3ds_chunk(OBJECT_MATERIAL)
+                       obj_material_chunk.add_variable("name", obj_material_names[i])
+                       obj_material_chunk.add_variable("face_list", obj_material_faces[i])
+                       face_chunk.add_subchunk(obj_material_chunk)
+       
+       return face_chunk
+
+def make_vert_chunk(vert_array):
+       '''Make a vertex chunk out of an array of vertices.'''
+       vert_chunk = _3ds_chunk(OBJECT_VERTICES)
+       vert_chunk.add_variable("vertices",vert_array)
+       return vert_chunk
+
+def make_uv_chunk(uv_array):
+       '''Make a UV chunk out of an array of UVs.'''
+       uv_chunk = _3ds_chunk(OBJECT_UV)
+       uv_chunk.add_variable("uv coords", uv_array)
+       return uv_chunk
+
+def make_mesh_chunk(mesh, materialDict):
+       '''Make a chunk out of a Blender mesh.'''
+       
+       # Extract the triangles from the mesh:
+       tri_list = extract_triangles(mesh)
+       
+       if mesh.faceUV:
+               # Remove the face UVs and convert it to vertex UV:
+               vert_array, uv_array, tri_list = remove_face_uv(mesh.verts, tri_list)
+       else:
+               # Add the vertices to the vertex array:
+               vert_array = _3ds_array()
+               for vert in mesh.verts:
+                       vert_array.add(_3ds_point_3d(vert.co))
+               # If the mesh has vertex UVs, create an array of UVs:
+               if mesh.vertexUV:
+                       uv_array = _3ds_array()
+                       for vert in mesh.verts:
+                               uv_array.add(_3ds_point_uv(vert.uvco))
+               else:
+                       # no UV at all:
+                       uv_array = None
+
+       # create the chunk:
+       mesh_chunk = _3ds_chunk(OBJECT_MESH)
+       
+       # add vertex chunk:
+       mesh_chunk.add_subchunk(make_vert_chunk(vert_array))
+       # add faces chunk:
+       
+       mesh_chunk.add_subchunk(make_faces_chunk(tri_list, mesh, materialDict))
+       
+       # if available, add uv chunk:
+       if uv_array:
+               mesh_chunk.add_subchunk(make_uv_chunk(uv_array))
+       
+       return mesh_chunk
+
+""" # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX
+def make_kfdata(start=0, stop=0, curtime=0):
+       '''Make the basic keyframe data chunk'''
+       kfdata = _3ds_chunk(KFDATA)
+       
+       kfhdr = _3ds_chunk(KFDATA_KFHDR)
+       kfhdr.add_variable("revision", _3ds_short(0))
+       # Not really sure what filename is used for, but it seems it is usually used
+       # to identify the program that generated the .3ds:
+       kfhdr.add_variable("filename", _3ds_string("Blender"))
+       kfhdr.add_variable("animlen", _3ds_int(stop-start))
+       
+       kfseg = _3ds_chunk(KFDATA_KFSEG)
+       kfseg.add_variable("start", _3ds_int(start))
+       kfseg.add_variable("stop", _3ds_int(stop))
+       
+       kfcurtime = _3ds_chunk(KFDATA_KFCURTIME)
+       kfcurtime.add_variable("curtime", _3ds_int(curtime))
+       
+       kfdata.add_subchunk(kfhdr)
+       kfdata.add_subchunk(kfseg)
+       kfdata.add_subchunk(kfcurtime)
+       return kfdata
+"""
+
+"""
+def make_track_chunk(ID, obj):
+       '''Make a chunk for track data.
+       
+       Depending on the ID, this will construct a position, rotation or scale track.'''
+       track_chunk = _3ds_chunk(ID)
+       track_chunk.add_variable("track_flags", _3ds_short())
+       track_chunk.add_variable("unknown", _3ds_int())
+       track_chunk.add_variable("unknown", _3ds_int())
+       track_chunk.add_variable("nkeys", _3ds_int(1))
+       # Next section should be repeated for every keyframe, but for now, animation is not actually supported.
+       track_chunk.add_variable("tcb_frame", _3ds_int(0))
+       track_chunk.add_variable("tcb_flags", _3ds_short())
+       if obj.type=='Empty':
+               if ID==POS_TRACK_TAG:
+                       # position vector:
+                       track_chunk.add_variable("position", _3ds_point_3d(obj.getLocation()))
+               elif ID==ROT_TRACK_TAG:
+                       # rotation (quaternion, angle first, followed by axis):
+                       q = obj.getEuler().toQuat()
+                       track_chunk.add_variable("rotation", _3ds_point_4d((q.angle, q.axis[0], q.axis[1], q.axis[2])))
+               elif ID==SCL_TRACK_TAG:
+                       # scale vector:
+                       track_chunk.add_variable("scale", _3ds_point_3d(obj.getSize()))
+       else:
+               # meshes have their transformations applied before 
+               # exporting, so write identity transforms here:
+               if ID==POS_TRACK_TAG:
+                       # position vector:
+                       track_chunk.add_variable("position", _3ds_point_3d((0.0,0.0,0.0)))
+               elif ID==ROT_TRACK_TAG:
+                       # rotation (quaternion, angle first, followed by axis):
+                       track_chunk.add_variable("rotation", _3ds_point_4d((0.0, 1.0, 0.0, 0.0)))
+               elif ID==SCL_TRACK_TAG:
+                       # scale vector:
+                       track_chunk.add_variable("scale", _3ds_point_3d((1.0, 1.0, 1.0)))
+       
+       return track_chunk
+"""
+
+"""
+def make_kf_obj_node(obj, name_to_id):
+       '''Make a node chunk for a Blender object.
+       
+       Takes the Blender object as a parameter. Object id's are taken from the dictionary name_to_id.
+       Blender Empty objects are converted to dummy nodes.'''
+       
+       name = obj.name
+       # main object node chunk:
+       kf_obj_node = _3ds_chunk(KFDATA_OBJECT_NODE_TAG)
+       # chunk for the object id: 
+       obj_id_chunk = _3ds_chunk(OBJECT_NODE_ID)
+       # object id is from the name_to_id dictionary:
+       obj_id_chunk.add_variable("node_id", _3ds_short(name_to_id[name]))
+       
+       # object node header:
+       obj_node_header_chunk = _3ds_chunk(OBJECT_NODE_HDR)
+       # object name:
+       if obj.type == 'Empty':
+               # Empties are called "$$$DUMMY" and use the OBJECT_INSTANCE_NAME chunk 
+               # for their name (see below):
+               obj_node_header_chunk.add_variable("name", _3ds_string("$$$DUMMY"))
+       else:
+               # Add the name:
+               obj_node_header_chunk.add_variable("name", _3ds_string(sane_name(name)))
+       # Add Flag variables (not sure what they do):
+       obj_node_header_chunk.add_variable("flags1", _3ds_short(0))
+       obj_node_header_chunk.add_variable("flags2", _3ds_short(0))
+       
+       # Check parent-child relationships:
+       parent = obj.parent
+       if (parent == None) or (parent.name not in name_to_id):
+               # If no parent, or the parents name is not in the name_to_id dictionary,
+               # parent id becomes -1:
+               obj_node_header_chunk.add_variable("parent", _3ds_short(-1))
+       else:
+               # Get the parent's id from the name_to_id dictionary:
+               obj_node_header_chunk.add_variable("parent", _3ds_short(name_to_id[parent.name]))
+       
+       # Add pivot chunk:
+       obj_pivot_chunk = _3ds_chunk(OBJECT_PIVOT)
+       obj_pivot_chunk.add_variable("pivot", _3ds_point_3d(obj.getLocation()))
+       kf_obj_node.add_subchunk(obj_pivot_chunk)
+       
+       # add subchunks for object id and node header:
+       kf_obj_node.add_subchunk(obj_id_chunk)
+       kf_obj_node.add_subchunk(obj_node_header_chunk)
+
+       # Empty objects need to have an extra chunk for the instance name:
+       if obj.type == 'Empty':
+               obj_instance_name_chunk = _3ds_chunk(OBJECT_INSTANCE_NAME)
+               obj_instance_name_chunk.add_variable("name", _3ds_string(sane_name(name)))
+               kf_obj_node.add_subchunk(obj_instance_name_chunk)
+       
+       # Add track chunks for position, rotation and scale:
+       kf_obj_node.add_subchunk(make_track_chunk(POS_TRACK_TAG, obj))
+       kf_obj_node.add_subchunk(make_track_chunk(ROT_TRACK_TAG, obj))
+       kf_obj_node.add_subchunk(make_track_chunk(SCL_TRACK_TAG, obj))
+
+       return kf_obj_node
+"""
+
+import BPyMessages
+def save_3ds(filename):
+       '''Save the Blender scene to a 3ds file.'''
+       # Time the export
+       
+       if not filename.lower().endswith('.3ds'):
+               filename += '.3ds'
+       
+       if not BPyMessages.Warning_SaveOver(filename):
+               return
+       
+       time1= Blender.sys.time()
+       Blender.Window.WaitCursor(1)
+       sce= bpy.data.scenes.active
+       
+       # Initialize the main chunk (primary):
+       primary = _3ds_chunk(PRIMARY)
+       # Add version chunk:
+       version_chunk = _3ds_chunk(VERSION)
+       version_chunk.add_variable("version", _3ds_int(3))
+       primary.add_subchunk(version_chunk)
+       
+       # init main object info chunk:
+       object_info = _3ds_chunk(OBJECTINFO)
+       
+       ''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX
+       # init main key frame data chunk:
+       kfdata = make_kfdata()
+       '''
+       
+       # Get all the supported objects selected in this scene:
+       # ob_sel= list(sce.objects.context)
+       # mesh_objects = [ (ob, me) for ob in ob_sel   for me in (BPyMesh.getMeshFromObject(ob, None, True, False, sce),) if me ]
+       # empty_objects = [ ob for ob in ob_sel if ob.type == 'Empty' ]
+       
+       # Make a list of all materials used in the selected meshes (use a dictionary,
+       # each material is added once):
+       materialDict = {}
+       mesh_objects = []
+       for ob in sce.objects.context:
+               for ob_derived, mat in getDerivedObjects(ob, False):
+                       data = getMeshFromObject(ob_derived, None, True, False, sce)
+                       if data:
+                               data.transform(mat, recalc_normals=False)
+                               mesh_objects.append((ob_derived, data))
+                               mat_ls = data.materials
+                               mat_ls_len = len(mat_ls)
+                               # get material/image tuples.
+                               if data.faceUV:
+                                       if not mat_ls:
+                                               mat = mat_name = None
+                                       
+                                       for f in data.faces:
+                                               if mat_ls:
+                                                       mat_index = f.mat
+                                                       if mat_index >= mat_ls_len:
+                                                               mat_index = f.mat = 0
+                                                       mat = mat_ls[mat_index]
+                                                       if mat: mat_name = mat.name
+                                                       else:   mat_name = None
+                                               # else there alredy set to none
+                                                       
+                                               img = f.image
+                                               if img: img_name = img.name
+                                               else:   img_name = None
+                                               
+                                               materialDict.setdefault((mat_name, img_name), (mat, img) )
+                                               
+                                       
+                               else:
+                                       for mat in mat_ls:
+                                               if mat: # material may be None so check its not.
+                                                       materialDict.setdefault((mat.name, None), (mat, None) )
+                                       
+                                       # Why 0 Why!
+                                       for f in data.faces:
+                                               if f.mat >= mat_ls_len:
+                                                       f.mat = 0 
+       
+       # Make material chunks for all materials used in the meshes:
+       for mat_and_image in materialDict.itervalues():
+               object_info.add_subchunk(make_material_chunk(mat_and_image[0], mat_and_image[1]))
+       
+       # Give all objects a unique ID and build a dictionary from object name to object id:
+       """
+       name_to_id = {}
+       for ob, data in mesh_objects:
+               name_to_id[ob.name]= len(name_to_id)
+       #for ob in empty_objects:
+       #       name_to_id[ob.name]= len(name_to_id)
+       """
+       
+       # Create object chunks for all meshes:
+       i = 0
+       for ob, blender_mesh in mesh_objects:
+               # create a new object chunk
+               object_chunk = _3ds_chunk(OBJECT)
+               
+               # set the object name
+               object_chunk.add_variable("name", _3ds_string(sane_name(ob.name)))
+               
+               # make a mesh chunk out of the mesh:
+               object_chunk.add_subchunk(make_mesh_chunk(blender_mesh, materialDict))
+               object_info.add_subchunk(object_chunk)
+               
+               ''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX
+               # make a kf object node for the object:
+               kfdata.add_subchunk(make_kf_obj_node(ob, name_to_id))
+               '''
+               blender_mesh.verts = None
+               i+=i
+
+       # Create chunks for all empties:
+       ''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX
+       for ob in empty_objects:
+               # Empties only require a kf object node:
+               kfdata.add_subchunk(make_kf_obj_node(ob, name_to_id))
+               pass
+       '''
+       
+       # Add main object info chunk to primary chunk:
+       primary.add_subchunk(object_info)
+       
+       ''' # COMMENTED OUT FOR 2.42 RELEASE!! CRASHES 3DS MAX
+       # Add main keyframe data chunk to primary chunk:
+       primary.add_subchunk(kfdata)
+       '''
+       
+       # At this point, the chunk hierarchy is completely built.
+       
+       # Check the size:
+       primary.get_size()
+       # Open the file for writing:
+       file = open( filename, 'wb' )
+       
+       # Recursively write the chunks to file:
+       primary.write(file)
+       
+       # Close the file:
+       file.close()
+       
+       # Debugging only: report the exporting time:
+       Blender.Window.WaitCursor(0)
+       print "3ds export time: %.2f" % (Blender.sys.time() - time1)
+       
+       # Debugging only: dump the chunk hierarchy:
+       #primary.dump()
+
+
+if __name__=='__main__':
+    if struct:
+        Blender.Window.FileSelector(save_3ds, "Export 3DS", Blender.sys.makename(ext='.3ds'))
+    else:
+        Blender.Draw.PupMenu("Error%t|This script requires a full python installation")
+# save_3ds('/test_b.3ds')
diff --git a/release/scripts/io/export_fbx.py b/release/scripts/io/export_fbx.py
new file mode 100644 (file)
index 0000000..50357cb
--- /dev/null
@@ -0,0 +1,3084 @@
+#!BPY
+"""
+Name: 'Autodesk FBX (.fbx)...'
+Blender: 249
+Group: 'Export'
+Tooltip: 'Selection to an ASCII Autodesk FBX '
+"""
+__author__ = "Campbell Barton"
+__url__ = ['www.blender.org', 'blenderartists.org']
+__version__ = "1.2"
+
+__bpydoc__ = """\
+This script is an exporter to the FBX file format.
+
+http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_fbx
+"""
+# --------------------------------------------------------------------------
+# FBX Export v0.1 by Campbell Barton (AKA Ideasman)
+# --------------------------------------------------------------------------
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# 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, MA  02111-1307, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+# --------------------------------------------------------------------------
+
+try:
+       import time
+       # import os # only needed for batch export, nbot used yet
+except:
+       time = None # use this to check if they have python modules installed
+
+# for python 2.3 support
+try:
+       set()
+except:
+       try:
+               from sets import Set as set
+       except:
+               set = None # so it complains you dont have a !
+
+# os is only needed for batch 'own dir' option
+try:
+       import os
+except:
+       os = None
+
+import Blender
+import bpy
+from Blender.Mathutils import Matrix, Vector, RotationMatrix
+
+import BPyObject
+import BPyMesh
+import BPySys
+import BPyMessages
+
+## This was used to make V, but faster not to do all that
+##valid = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_,.()[]{}'
+##v = range(255)
+##for c in valid: v.remove(ord(c))
+v = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,42,43,47,58,59,60,61,62,63,64,92,94,96,124,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254]
+invalid = ''.join([chr(i) for i in v])
+def cleanName(name):
+       for ch in invalid:      name = name.replace(ch, '_')
+       return name
+del v, i
+
+
+def copy_file(source, dest):
+       file = open(source, 'rb')
+       data = file.read()
+       file.close()
+       
+       file = open(dest, 'wb')
+       file.write(data)
+       file.close()
+
+
+def copy_images(dest_dir, textures):
+       if not dest_dir.endswith(Blender.sys.sep):
+               dest_dir += Blender.sys.sep
+       
+       image_paths = set()
+       for tex in textures:
+               image_paths.add(Blender.sys.expandpath(tex.filename))
+       
+       # Now copy images
+       copyCount = 0
+       for image_path in image_paths:
+               if Blender.sys.exists(image_path):
+                       # Make a name for the target path.
+                       dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1]
+                       if not Blender.sys.exists(dest_image_path): # Image isnt alredy there
+                               print '\tCopying "%s" > "%s"' % (image_path, dest_image_path)
+                               try:
+                                       copy_file(image_path, dest_image_path)
+                                       copyCount+=1
+                               except:
+                                       print '\t\tWarning, file failed to copy, skipping.'
+       
+       print '\tCopied %d images' % copyCount
+
+mtx4_identity = Matrix()
+
+# testing
+mtx_x90                = RotationMatrix( 90, 3, 'x') # used
+#mtx_x90n      = RotationMatrix(-90, 3, 'x')
+#mtx_y90       = RotationMatrix( 90, 3, 'y')
+#mtx_y90n      = RotationMatrix(-90, 3, 'y')
+#mtx_z90       = RotationMatrix( 90, 3, 'z')
+#mtx_z90n      = RotationMatrix(-90, 3, 'z')
+
+#mtx4_x90      = RotationMatrix( 90, 4, 'x')
+mtx4_x90n      = RotationMatrix(-90, 4, 'x') # used
+#mtx4_y90      = RotationMatrix( 90, 4, 'y')
+mtx4_y90n      = RotationMatrix(-90, 4, 'y') # used
+mtx4_z90       = RotationMatrix( 90, 4, 'z') # used
+mtx4_z90n      = RotationMatrix(-90, 4, 'z') # used
+
+def strip_path(p):
+       return p.split('\\')[-1].split('/')[-1]
+
+# Used to add the scene name into the filename without using odd chars 
+sane_name_mapping_ob = {}
+sane_name_mapping_mat = {}
+sane_name_mapping_tex = {}
+sane_name_mapping_take = {}
+sane_name_mapping_group = {}
+
+# Make sure reserved names are not used
+sane_name_mapping_ob['Scene'] = 'Scene_'
+sane_name_mapping_ob['blend_root'] = 'blend_root_'
+
+def increment_string(t):
+       name = t
+       num = ''
+       while name and name[-1].isdigit():
+               num = name[-1] + num
+               name = name[:-1]
+       if num: return '%s%d' % (name, int(num)+1)      
+       else:   return name + '_0'
+
+
+
+# todo - Disallow the name 'Scene' and 'blend_root' - it will bugger things up.
+def sane_name(data, dct):
+       #if not data: return None
+       
+       if type(data)==tuple: # materials are paired up with images
+               data, other = data
+               use_other = True
+       else:
+               other = None
+               use_other = False
+       
+       if data:        name = data.name
+       else:           name = None
+       orig_name = name
+       
+       if other:
+               orig_name_other = other.name
+               name = '%s #%s' % (name, orig_name_other)
+       else:
+               orig_name_other = None
+       
+       # dont cache, only ever call once for each data type now,
+       # so as to avoid namespace collision between types - like with objects <-> bones
+       #try:           return dct[name]
+       #except:                pass
+       
+       if not name:
+               name = 'unnamed' # blank string, ASKING FOR TROUBLE!
+       else:
+               #name = BPySys.cleanName(name)
+               name = cleanName(name) # use our own
+       
+       while name in dct.itervalues(): name = increment_string(name)
+       
+       if use_other: # even if other is None - orig_name_other will be a string or None
+               dct[orig_name, orig_name_other] = name
+       else:
+               dct[orig_name] = name
+               
+       return name
+
+def sane_obname(data):         return sane_name(data, sane_name_mapping_ob)
+def sane_matname(data):                return sane_name(data, sane_name_mapping_mat)
+def sane_texname(data):                return sane_name(data, sane_name_mapping_tex)
+def sane_takename(data):       return sane_name(data, sane_name_mapping_take)
+def sane_groupname(data):      return sane_name(data, sane_name_mapping_group)
+
+def derived_paths(fname_orig, basepath, FORCE_CWD=False):
+       '''
+       fname_orig - blender path, can be relative
+       basepath - fname_rel will be relative to this
+       FORCE_CWD - dont use the basepath, just add a ./ to the filename.
+               use when we know the file will be in the basepath.
+       '''
+       fname = Blender.sys.expandpath(fname_orig)
+       fname_strip = strip_path(fname)
+       if FORCE_CWD:   fname_rel = '.' + Blender.sys.sep + fname_strip
+       else:                           fname_rel = Blender.sys.relpath(fname, basepath)
+       if fname_rel.startswith('//'): fname_rel = '.' + Blender.sys.sep + fname_rel[2:]
+       return fname, fname_strip, fname_rel
+
+
+def mat4x4str(mat):
+       return '%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f' % tuple([ f for v in mat for f in v ])
+
+def meshNormalizedWeights(me):
+       try: # account for old bad BPyMesh
+               groupNames, vWeightList = BPyMesh.meshWeight2List(me)
+       except:
+               return [],[]
+       
+       if not groupNames:
+               return [],[]
+       
+       for i, vWeights in enumerate(vWeightList):
+               tot = 0.0
+               for w in vWeights:
+                       tot+=w
+               
+               if tot:
+                       for j, w in enumerate(vWeights):
+                               vWeights[j] = w/tot
+       
+       return groupNames, vWeightList
+
+header_comment = \
+'''; FBX 6.1.0 project file
+; Created by Blender FBX Exporter
+; for support mail: ideasman42@gmail.com
+; ----------------------------------------------------
+
+'''
+
+# This func can be called with just the filename
+def write(filename, batch_objects = None, \
+               EXP_OBS_SELECTED =                      True,
+               EXP_MESH =                                      True,
+               EXP_MESH_APPLY_MOD =            True,
+               EXP_MESH_HQ_NORMALS =           False,
+               EXP_ARMATURE =                          True,
+               EXP_LAMP =                                      True,
+               EXP_CAMERA =                            True,
+               EXP_EMPTY =                                     True,
+               EXP_IMAGE_COPY =                        False,
+               GLOBAL_MATRIX =                         Matrix(),
+               ANIM_ENABLE =                           True,
+               ANIM_OPTIMIZE =                         True,
+               ANIM_OPTIMIZE_PRECISSION =      6,
+               ANIM_ACTION_ALL =                       False,
+               BATCH_ENABLE =                          False,
+               BATCH_GROUP =                           True,
+               BATCH_SCENE =                           False,
+               BATCH_FILE_PREFIX =                     '',
+               BATCH_OWN_DIR =                         False
+       ):
+       
+       # ----------------- Batch support!
+       if BATCH_ENABLE:
+               if os == None:  BATCH_OWN_DIR = False
+               
+               fbxpath = filename
+               
+               # get the path component of filename
+               tmp_exists = Blender.sys.exists(fbxpath)
+               
+               if tmp_exists != 2: # a file, we want a path
+                       while fbxpath and fbxpath[-1] not in ('/', '\\'):
+                               fbxpath = fbxpath[:-1]
+                       if not filename:
+                               Draw.PupMenu('Error%t|Directory does not exist!')
+                               return
+                       
+                       tmp_exists = Blender.sys.exists(fbxpath)
+               
+               if tmp_exists != 2:
+                       Draw.PupMenu('Error%t|Directory does not exist!')
+                       return
+               
+               if not fbxpath.endswith(Blender.sys.sep):
+                       fbxpath += Blender.sys.sep
+               del tmp_exists
+               
+               
+               if BATCH_GROUP:
+                       data_seq = bpy.data.groups
+               else:
+                       data_seq = bpy.data.scenes
+               
+               # call this function within a loop with BATCH_ENABLE == False
+               orig_sce = bpy.data.scenes.active
+               
+               
+               new_fbxpath = fbxpath # own dir option modifies, we need to keep an original
+               for data in data_seq: # scene or group
+                       newname = BATCH_FILE_PREFIX + BPySys.cleanName(data.name)
+                       
+                       
+                       if BATCH_OWN_DIR:
+                               new_fbxpath = fbxpath + newname + Blender.sys.sep
+                               # path may alredy exist
+                               # TODO - might exist but be a file. unlikely but should probably account for it.
+                               
+                               if Blender.sys.exists(new_fbxpath) == 0:
+                                       os.mkdir(new_fbxpath)
+                               
+                       
+                       filename = new_fbxpath + newname + '.fbx'
+                       
+                       print '\nBatch exporting %s as...\n\t"%s"' % (data, filename)
+                       
+                       if BATCH_GROUP: #group
+                               # group, so objects update properly, add a dummy scene.
+                               sce = bpy.data.scenes.new()
+                               sce.Layers = (1<<20) -1
+                               bpy.data.scenes.active = sce
+                               for ob_base in data.objects:
+                                       sce.objects.link(ob_base)
+                               
+                               sce.update(1)
+                               
+                               # TODO - BUMMER! Armatures not in the group wont animate the mesh
+                               
+                       else:# scene
+                               
+                               
+                               data_seq.active = data
+                       
+                       
+                       # Call self with modified args
+                       # Dont pass batch options since we alredy usedt them
+                       write(filename, data.objects,
+                               False,
+                               EXP_MESH,
+                               EXP_MESH_APPLY_MOD,
+                               EXP_MESH_HQ_NORMALS,
+                               EXP_ARMATURE,
+                               EXP_LAMP,
+                               EXP_CAMERA,
+                               EXP_EMPTY,
+                               EXP_IMAGE_COPY,
+                               GLOBAL_MATRIX,
+                               ANIM_ENABLE,
+                               ANIM_OPTIMIZE,
+                               ANIM_OPTIMIZE_PRECISSION,
+                               ANIM_ACTION_ALL
+                       )
+                       
+                       if BATCH_GROUP:
+                               # remove temp group scene
+                               bpy.data.scenes.unlink(sce)
+               
+               bpy.data.scenes.active = orig_sce
+               
+               return # so the script wont run after we have batch exported.
+       
+       # end batch support
+       
+       # Use this for working out paths relative to the export location
+       basepath = Blender.sys.dirname(filename)
+       
+       # ----------------------------------------------
+       # storage classes
+       class my_bone_class:
+               __slots__ =(\
+                 'blenName',\
+                 'blenBone',\
+                 'blenMeshes',\
+                 'restMatrix',\
+                 'parent',\
+                 'blenName',\
+                 'fbxName',\
+                 'fbxArm',\
+                 '__pose_bone',\
+                 '__anim_poselist')
+               
+               def __init__(self, blenBone, fbxArm):
+                       
+                       # This is so 2 armatures dont have naming conflicts since FBX bones use object namespace
+                       self.fbxName = sane_obname(blenBone)
+                       
+                       self.blenName =                 blenBone.name
+                       self.blenBone =                 blenBone
+                       self.blenMeshes =               {}                                      # fbxMeshObName : mesh
+                       self.fbxArm =                   fbxArm
+                       self.restMatrix =               blenBone.matrix['ARMATURESPACE']
+                       
+                       # not used yet
+                       # self.restMatrixInv =  self.restMatrix.copy().invert()
+                       # self.restMatrixLocal =        None # set later, need parent matrix
+                       
+                       self.parent =                   None
+                       
+                       # not public
+                       pose = fbxArm.blenObject.getPose()
+                       self.__pose_bone =              pose.bones[self.blenName]
+                       
+                       # store a list if matricies here, (poseMatrix, head, tail)
+                       # {frame:posematrix, frame:posematrix, ...}
+                       self.__anim_poselist = {}
+               
+               '''
+               def calcRestMatrixLocal(self):
+                       if self.parent:
+                               self.restMatrixLocal = self.restMatrix * self.parent.restMatrix.copy().invert()
+                       else:
+                               self.restMatrixLocal = self.restMatrix.copy()
+               '''
+               def setPoseFrame(self, f):
+                       # cache pose info here, frame must be set beforehand
+                       
+                       # Didnt end up needing head or tail, if we do - here it is.
+                       '''
+                       self.__anim_poselist[f] = (\
+                               self.__pose_bone.poseMatrix.copy(),\
+                               self.__pose_bone.head.copy(),\
+                               self.__pose_bone.tail.copy() )
+                       '''
+                       
+                       self.__anim_poselist[f] = self.__pose_bone.poseMatrix.copy()
+               
+               # get pose from frame.
+               def getPoseMatrix(self, f):# ----------------------------------------------
+                       return self.__anim_poselist[f]
+               '''
+               def getPoseHead(self, f):
+                       #return self.__pose_bone.head.copy()
+                       return self.__anim_poselist[f][1].copy()
+               def getPoseTail(self, f):
+                       #return self.__pose_bone.tail.copy()
+                       return self.__anim_poselist[f][2].copy()
+               '''
+               # end
+               
+               def getAnimParRelMatrix(self, frame):
+                       #arm_mat = self.fbxArm.matrixWorld
+                       #arm_mat = self.fbxArm.parRelMatrix()
+                       if not self.parent:
+                               #return mtx4_z90 * (self.getPoseMatrix(frame) * arm_mat) # dont apply arm matrix anymore
+                               return mtx4_z90 * self.getPoseMatrix(frame)
+                       else:
+                               #return (mtx4_z90 * ((self.getPoseMatrix(frame) * arm_mat)))  *  (mtx4_z90 * (self.parent.getPoseMatrix(frame) * arm_mat)).invert()
+                               return (mtx4_z90 * (self.getPoseMatrix(frame)))  *  (mtx4_z90 * self.parent.getPoseMatrix(frame)).invert()
+               
+               # we need thes because cameras and lights modified rotations
+               def getAnimParRelMatrixRot(self, frame):
+                       return self.getAnimParRelMatrix(frame)
+               
+               def flushAnimData(self):
+                       self.__anim_poselist.clear()
+
+
+       class my_object_generic:
+               # Other settings can be applied for each type - mesh, armature etc.
+               def __init__(self, ob, matrixWorld = None):
+                       self.fbxName = sane_obname(ob)
+                       self.blenObject = ob
+                       self.fbxGroupNames = []
+                       self.fbxParent = None # set later on IF the parent is in the selection.
+                       if matrixWorld:         self.matrixWorld = matrixWorld * GLOBAL_MATRIX
+                       else:                           self.matrixWorld = ob.matrixWorld * GLOBAL_MATRIX
+                       self.__anim_poselist = {} # we should only access this
+               
+               def parRelMatrix(self):
+                       if self.fbxParent:
+                               return self.matrixWorld * self.fbxParent.matrixWorld.copy().invert()
+                       else:
+                               return self.matrixWorld
+               
+               def setPoseFrame(self, f):
+                       self.__anim_poselist[f] =  self.blenObject.matrixWorld.copy()
+               
+               def getAnimParRelMatrix(self, frame):
+                       if self.fbxParent:
+                               #return (self.__anim_poselist[frame] * self.fbxParent.__anim_poselist[frame].copy().invert() ) * GLOBAL_MATRIX
+                               return (self.__anim_poselist[frame] * GLOBAL_MATRIX) * (self.fbxParent.__anim_poselist[frame] * GLOBAL_MATRIX).invert()
+                       else:
+                               return self.__anim_poselist[frame] * GLOBAL_MATRIX
+               
+               def getAnimParRelMatrixRot(self, frame):
+                       type = self.blenObject.type
+                       if self.fbxParent:
+                               matrix_rot = (((self.__anim_poselist[frame] * GLOBAL_MATRIX) * (self.fbxParent.__anim_poselist[frame] * GLOBAL_MATRIX).invert())).rotationPart()
+                       else:
+                               matrix_rot = (self.__anim_poselist[frame] * GLOBAL_MATRIX).rotationPart()
+                       
+                       # Lamps need to be rotated
+                       if type =='Lamp':
+                               matrix_rot = mtx_x90 * matrix_rot
+                       elif ob and type =='Camera':
+                               y = Vector(0,1,0) * matrix_rot
+                               matrix_rot = matrix_rot * RotationMatrix(90, 3, 'r', y)
+                       
+                       return matrix_rot
+                       
+       # ----------------------------------------------
+       
+       
+       
+       
+       
+       print '\nFBX export starting...', filename
+       start_time = Blender.sys.time()
+       try:
+               file = open(filename, 'w')
+       except:
+               return False
+       
+       sce = bpy.data.scenes.active
+       world = sce.world
+       
+       
+       # ---------------------------- Write the header first
+       file.write(header_comment)
+       if time:
+               curtime = time.localtime()[0:6]
+       else:
+               curtime = (0,0,0,0,0,0)
+       # 
+       file.write(\
+'''FBXHeaderExtension:  {
+       FBXHeaderVersion: 1003
+       FBXVersion: 6100
+       CreationTimeStamp:  {
+               Version: 1000
+               Year: %.4i
+               Month: %.2i
+               Day: %.2i
+               Hour: %.2i
+               Minute: %.2i
+               Second: %.2i
+               Millisecond: 0
+       }
+       Creator: "FBX SDK/FBX Plugins build 20070228"
+       OtherFlags:  {
+               FlagPLE: 0
+       }
+}''' % (curtime))
+       
+       file.write('\nCreationTime: "%.4i-%.2i-%.2i %.2i:%.2i:%.2i:000"' % curtime)
+       file.write('\nCreator: "Blender3D version %.2f"' % Blender.Get('version'))
+       
+       pose_items = [] # list of (fbxName, matrix) to write pose data for, easier to collect allong the way
+       
+       # --------------- funcs for exporting
+       def object_tx(ob, loc, matrix, matrix_mod = None):
+               '''
+               Matrix mod is so armature objects can modify their bone matricies
+               '''
+               if isinstance(ob, Blender.Types.BoneType):
+                       
+                       # we know we have a matrix
+                       # matrix = mtx4_z90 * (ob.matrix['ARMATURESPACE'] * matrix_mod)
+                       matrix = mtx4_z90 * ob.matrix['ARMATURESPACE'] # dont apply armature matrix anymore
+                       
+                       parent = ob.parent
+                       if parent:
+                               #par_matrix = mtx4_z90 * (parent.matrix['ARMATURESPACE'] * matrix_mod)
+                               par_matrix = mtx4_z90 * parent.matrix['ARMATURESPACE'] # dont apply armature matrix anymore
+                               matrix = matrix * par_matrix.copy().invert()
+                               
+                       matrix_rot =    matrix.rotationPart()
+                       
+                       loc =                   tuple(matrix.translationPart())
+                       scale =                 tuple(matrix.scalePart())
+                       rot =                   tuple(matrix_rot.toEuler())
+                       
+               else:
+                       # This is bad because we need the parent relative matrix from the fbx parent (if we have one), dont use anymore
+                       #if ob and not matrix: matrix = ob.matrixWorld * GLOBAL_MATRIX
+                       if ob and not matrix: raise "error: this should never happen!"
+                       
+                       matrix_rot = matrix
+                       #if matrix:
+                       #       matrix = matrix_scale * matrix
+                       
+                       if matrix:
+                               loc = tuple(matrix.translationPart())
+                               scale = tuple(matrix.scalePart())
+                               
+                               matrix_rot = matrix.rotationPart()
+                               # Lamps need to be rotated
+                               if ob and ob.type =='Lamp':
+                                       matrix_rot = mtx_x90 * matrix_rot
+                                       rot = tuple(matrix_rot.toEuler())
+                               elif ob and ob.type =='Camera':
+                                       y = Vector(0,1,0) * matrix_rot
+                                       matrix_rot = matrix_rot * RotationMatrix(90, 3, 'r', y)
+                                       rot = tuple(matrix_rot.toEuler())
+                               else:
+                                       rot = tuple(matrix_rot.toEuler())
+                       else:
+                               if not loc:
+                                       loc = 0,0,0
+                               scale = 1,1,1
+                               rot = 0,0,0
+               
+               return loc, rot, scale, matrix, matrix_rot
+       
+       def write_object_tx(ob, loc, matrix, matrix_mod= None):
+               '''
+               We have loc to set the location if non blender objects that have a location
+               
+               matrix_mod is only used for bones at the moment
+               '''
+               loc, rot, scale, matrix, matrix_rot = object_tx(ob, loc, matrix, matrix_mod)
+               
+               file.write('\n\t\t\tProperty: "Lcl Translation", "Lcl Translation", "A+",%.15f,%.15f,%.15f' % loc)
+               file.write('\n\t\t\tProperty: "Lcl Rotation", "Lcl Rotation", "A+",%.15f,%.15f,%.15f' % rot)
+               file.write('\n\t\t\tProperty: "Lcl Scaling", "Lcl Scaling", "A+",%.15f,%.15f,%.15f' % scale)
+               return loc, rot, scale, matrix, matrix_rot
+       
+       def write_object_props(ob=None, loc=None, matrix=None, matrix_mod=None):
+               # if the type is 0 its an empty otherwise its a mesh
+               # only difference at the moment is one has a color
+               file.write('''
+               Properties60:  {
+                       Property: "QuaternionInterpolate", "bool", "",0
+                       Property: "Visibility", "Visibility", "A+",1''')
+               
+               loc, rot, scale, matrix, matrix_rot = write_object_tx(ob, loc, matrix, matrix_mod)
+               
+               # Rotation order, note, for FBX files Iv loaded normal order is 1
+               # setting to zero.
+               # eEULER_XYZ = 0
+               # eEULER_XZY
+               # eEULER_YZX
+               # eEULER_YXZ
+               # eEULER_ZXY
+               # eEULER_ZYX
+               
+               file.write('''
+                       Property: "RotationOffset", "Vector3D", "",0,0,0
+                       Property: "RotationPivot", "Vector3D", "",0,0,0
+                       Property: "ScalingOffset", "Vector3D", "",0,0,0
+                       Property: "ScalingPivot", "Vector3D", "",0,0,0
+                       Property: "TranslationActive", "bool", "",0
+                       Property: "TranslationMin", "Vector3D", "",0,0,0
+                       Property: "TranslationMax", "Vector3D", "",0,0,0
+                       Property: "TranslationMinX", "bool", "",0
+                       Property: "TranslationMinY", "bool", "",0
+                       Property: "TranslationMinZ", "bool", "",0
+                       Property: "TranslationMaxX", "bool", "",0
+                       Property: "TranslationMaxY", "bool", "",0
+                       Property: "TranslationMaxZ", "bool", "",0
+                       Property: "RotationOrder", "enum", "",0
+                       Property: "RotationSpaceForLimitOnly", "bool", "",0
+                       Property: "AxisLen", "double", "",10
+                       Property: "PreRotation", "Vector3D", "",0,0,0
+                       Property: "PostRotation", "Vector3D", "",0,0,0
+                       Property: "RotationActive", "bool", "",0
+                       Property: "RotationMin", "Vector3D", "",0,0,0
+                       Property: "RotationMax", "Vector3D", "",0,0,0
+                       Property: "RotationMinX", "bool", "",0
+                       Property: "RotationMinY", "bool", "",0
+                       Property: "RotationMinZ", "bool", "",0
+                       Property: "RotationMaxX", "bool", "",0
+                       Property: "RotationMaxY", "bool", "",0
+                       Property: "RotationMaxZ", "bool", "",0
+                       Property: "RotationStiffnessX", "double", "",0
+                       Property: "RotationStiffnessY", "double", "",0
+                       Property: "RotationStiffnessZ", "double", "",0
+                       Property: "MinDampRangeX", "double", "",0
+                       Property: "MinDampRangeY", "double", "",0
+                       Property: "MinDampRangeZ", "double", "",0
+                       Property: "MaxDampRangeX", "double", "",0
+                       Property: "MaxDampRangeY", "double", "",0
+                       Property: "MaxDampRangeZ", "double", "",0
+                       Property: "MinDampStrengthX", "double", "",0
+                       Property: "MinDampStrengthY", "double", "",0
+                       Property: "MinDampStrengthZ", "double", "",0
+                       Property: "MaxDampStrengthX", "double", "",0
+                       Property: "MaxDampStrengthY", "double", "",0
+                       Property: "MaxDampStrengthZ", "double", "",0
+                       Property: "PreferedAngleX", "double", "",0
+                       Property: "PreferedAngleY", "double", "",0
+                       Property: "PreferedAngleZ", "double", "",0
+                       Property: "InheritType", "enum", "",0
+                       Property: "ScalingActive", "bool", "",0
+                       Property: "ScalingMin", "Vector3D", "",1,1,1
+                       Property: "ScalingMax", "Vector3D", "",1,1,1
+                       Property: "ScalingMinX", "bool", "",0
+                       Property: "ScalingMinY", "bool", "",0
+                       Property: "ScalingMinZ", "bool", "",0
+                       Property: "ScalingMaxX", "bool", "",0
+                       Property: "ScalingMaxY", "bool", "",0
+                       Property: "ScalingMaxZ", "bool", "",0
+                       Property: "GeometricTranslation", "Vector3D", "",0,0,0
+                       Property: "GeometricRotation", "Vector3D", "",0,0,0
+                       Property: "GeometricScaling", "Vector3D", "",1,1,1
+                       Property: "LookAtProperty", "object", ""
+                       Property: "UpVectorProperty", "object", ""
+                       Property: "Show", "bool", "",1
+                       Property: "NegativePercentShapeSupport", "bool", "",1
+                       Property: "DefaultAttributeIndex", "int", "",0''')
+               if ob and type(ob) != Blender.Types.BoneType:
+                       # Only mesh objects have color 
+                       file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8')
+                       file.write('\n\t\t\tProperty: "Size", "double", "",100')
+                       file.write('\n\t\t\tProperty: "Look", "enum", "",1')
+               
+               return loc, rot, scale, matrix, matrix_rot
+       
+       
+       # -------------------------------------------- Armatures
+       #def write_bone(bone, name, matrix_mod):
+       def write_bone(my_bone):
+               file.write('\n\tModel: "Model::%s", "Limb" {' % my_bone.fbxName)
+               file.write('\n\t\tVersion: 232')
+               
+               #poseMatrix = write_object_props(my_bone.blenBone, None, None, my_bone.fbxArm.parRelMatrix())[3]
+               poseMatrix = write_object_props(my_bone.blenBone)[3] # dont apply bone matricies anymore
+               pose_items.append( (my_bone.fbxName, poseMatrix) )
+               
+               
+               # file.write('\n\t\t\tProperty: "Size", "double", "",%.6f' % ((my_bone.blenData.head['ARMATURESPACE'] - my_bone.blenData.tail['ARMATURESPACE']) * my_bone.fbxArm.parRelMatrix()).length)
+               file.write('\n\t\t\tProperty: "Size", "double", "",1')
+               
+               #((my_bone.blenData.head['ARMATURESPACE'] * my_bone.fbxArm.matrixWorld) - (my_bone.blenData.tail['ARMATURESPACE'] * my_bone.fbxArm.parRelMatrix())).length)
+               
+               """
+               file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' %\
+                       ((my_bone.blenBone.head['ARMATURESPACE'] - my_bone.blenBone.tail['ARMATURESPACE']) * my_bone.fbxArm.parRelMatrix()).length)
+               """
+               
+               file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' %\
+                       (my_bone.blenBone.head['ARMATURESPACE'] - my_bone.blenBone.tail['ARMATURESPACE']).length)
+               
+               #file.write('\n\t\t\tProperty: "LimbLength", "double", "",1')
+               file.write('\n\t\t\tProperty: "Color", "ColorRGB", "",0.8,0.8,0.8')
+               file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8')
+               file.write('\n\t\t}')
+               file.write('\n\t\tMultiLayer: 0')
+               file.write('\n\t\tMultiTake: 1')
+               file.write('\n\t\tShading: Y')
+               file.write('\n\t\tCulling: "CullingOff"')
+               file.write('\n\t\tTypeFlags: "Skeleton"')
+               file.write('\n\t}')
+       
+       def write_camera_switch():
+               file.write('''
+       Model: "Model::Camera Switcher", "CameraSwitcher" {
+               Version: 232''')
+               
+               write_object_props()
+               file.write('''
+                       Property: "Color", "Color", "A",0.8,0.8,0.8
+                       Property: "Camera Index", "Integer", "A+",100
+               }
+               MultiLayer: 0
+               MultiTake: 1
+               Hidden: "True"
+               Shading: W
+               Culling: "CullingOff"
+               Version: 101
+               Name: "Model::Camera Switcher"
+               CameraId: 0
+               CameraName: 100
+               CameraIndexName: 
+       }''')
+       
+       def write_camera_dummy(name, loc, near, far, proj_type, up):
+               file.write('\n\tModel: "Model::%s", "Camera" {' % name )
+               file.write('\n\t\tVersion: 232')
+               write_object_props(None, loc)
+               
+               file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8')
+               file.write('\n\t\t\tProperty: "Roll", "Roll", "A+",0')
+               file.write('\n\t\t\tProperty: "FieldOfView", "FieldOfView", "A+",40')
+               file.write('\n\t\t\tProperty: "FieldOfViewX", "FieldOfView", "A+",1')
+               file.write('\n\t\t\tProperty: "FieldOfViewY", "FieldOfView", "A+",1')
+               file.write('\n\t\t\tProperty: "OpticalCenterX", "Real", "A+",0')
+               file.write('\n\t\t\tProperty: "OpticalCenterY", "Real", "A+",0')
+               file.write('\n\t\t\tProperty: "BackgroundColor", "Color", "A+",0.63,0.63,0.63')
+               file.write('\n\t\t\tProperty: "TurnTable", "Real", "A+",0')
+               file.write('\n\t\t\tProperty: "DisplayTurnTableIcon", "bool", "",1')
+               file.write('\n\t\t\tProperty: "Motion Blur Intensity", "Real", "A+",1')
+               file.write('\n\t\t\tProperty: "UseMotionBlur", "bool", "",0')
+               file.write('\n\t\t\tProperty: "UseRealTimeMotionBlur", "bool", "",1')
+               file.write('\n\t\t\tProperty: "ResolutionMode", "enum", "",0')
+               file.write('\n\t\t\tProperty: "ApertureMode", "enum", "",2')
+               file.write('\n\t\t\tProperty: "GateFit", "enum", "",0')
+               file.write('\n\t\t\tProperty: "FocalLength", "Real", "A+",21.3544940948486')
+               file.write('\n\t\t\tProperty: "CameraFormat", "enum", "",0')
+               file.write('\n\t\t\tProperty: "AspectW", "double", "",320')
+               file.write('\n\t\t\tProperty: "AspectH", "double", "",200')
+               file.write('\n\t\t\tProperty: "PixelAspectRatio", "double", "",1')
+               file.write('\n\t\t\tProperty: "UseFrameColor", "bool", "",0')
+               file.write('\n\t\t\tProperty: "FrameColor", "ColorRGB", "",0.3,0.3,0.3')
+               file.write('\n\t\t\tProperty: "ShowName", "bool", "",1')
+               file.write('\n\t\t\tProperty: "ShowGrid", "bool", "",1')
+               file.write('\n\t\t\tProperty: "ShowOpticalCenter", "bool", "",0')
+               file.write('\n\t\t\tProperty: "ShowAzimut", "bool", "",1')
+               file.write('\n\t\t\tProperty: "ShowTimeCode", "bool", "",0')
+               file.write('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % near)
+               file.write('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % far)
+               file.write('\n\t\t\tProperty: "FilmWidth", "double", "",0.816')
+               file.write('\n\t\t\tProperty: "FilmHeight", "double", "",0.612')
+               file.write('\n\t\t\tProperty: "FilmAspectRatio", "double", "",1.33333333333333')
+               file.write('\n\t\t\tProperty: "FilmSqueezeRatio", "double", "",1')
+               file.write('\n\t\t\tProperty: "FilmFormatIndex", "enum", "",4')
+               file.write('\n\t\t\tProperty: "ViewFrustum", "bool", "",1')
+               file.write('\n\t\t\tProperty: "ViewFrustumNearFarPlane", "bool", "",0')
+               file.write('\n\t\t\tProperty: "ViewFrustumBackPlaneMode", "enum", "",2')
+               file.write('\n\t\t\tProperty: "BackPlaneDistance", "double", "",100')
+               file.write('\n\t\t\tProperty: "BackPlaneDistanceMode", "enum", "",0')
+               file.write('\n\t\t\tProperty: "ViewCameraToLookAt", "bool", "",1')
+               file.write('\n\t\t\tProperty: "LockMode", "bool", "",0')
+               file.write('\n\t\t\tProperty: "LockInterestNavigation", "bool", "",0')
+               file.write('\n\t\t\tProperty: "FitImage", "bool", "",0')
+               file.write('\n\t\t\tProperty: "Crop", "bool", "",0')
+               file.write('\n\t\t\tProperty: "Center", "bool", "",1')
+               file.write('\n\t\t\tProperty: "KeepRatio", "bool", "",1')
+               file.write('\n\t\t\tProperty: "BackgroundMode", "enum", "",0')
+               file.write('\n\t\t\tProperty: "BackgroundAlphaTreshold", "double", "",0.5')
+               file.write('\n\t\t\tProperty: "ForegroundTransparent", "bool", "",1')
+               file.write('\n\t\t\tProperty: "DisplaySafeArea", "bool", "",0')
+               file.write('\n\t\t\tProperty: "SafeAreaDisplayStyle", "enum", "",1')
+               file.write('\n\t\t\tProperty: "SafeAreaAspectRatio", "double", "",1.33333333333333')
+               file.write('\n\t\t\tProperty: "Use2DMagnifierZoom", "bool", "",0')
+               file.write('\n\t\t\tProperty: "2D Magnifier Zoom", "Real", "A+",100')
+               file.write('\n\t\t\tProperty: "2D Magnifier X", "Real", "A+",50')
+               file.write('\n\t\t\tProperty: "2D Magnifier Y", "Real", "A+",50')
+               file.write('\n\t\t\tProperty: "CameraProjectionType", "enum", "",%i' % proj_type)
+               file.write('\n\t\t\tProperty: "UseRealTimeDOFAndAA", "bool", "",0')
+               file.write('\n\t\t\tProperty: "UseDepthOfField", "bool", "",0')
+               file.write('\n\t\t\tProperty: "FocusSource", "enum", "",0')
+               file.write('\n\t\t\tProperty: "FocusAngle", "double", "",3.5')
+               file.write('\n\t\t\tProperty: "FocusDistance", "double", "",200')
+               file.write('\n\t\t\tProperty: "UseAntialiasing", "bool", "",0')
+               file.write('\n\t\t\tProperty: "AntialiasingIntensity", "double", "",0.77777')
+               file.write('\n\t\t\tProperty: "UseAccumulationBuffer", "bool", "",0')
+               file.write('\n\t\t\tProperty: "FrameSamplingCount", "int", "",7')
+               file.write('\n\t\t}')
+               file.write('\n\t\tMultiLayer: 0')
+               file.write('\n\t\tMultiTake: 0')
+               file.write('\n\t\tHidden: "True"')
+               file.write('\n\t\tShading: Y')
+               file.write('\n\t\tCulling: "CullingOff"')
+               file.write('\n\t\tTypeFlags: "Camera"')
+               file.write('\n\t\tGeometryVersion: 124')
+               file.write('\n\t\tPosition: %.6f,%.6f,%.6f' % loc)
+               file.write('\n\t\tUp: %i,%i,%i' % up)
+               file.write('\n\t\tLookAt: 0,0,0')
+               file.write('\n\t\tShowInfoOnMoving: 1')
+               file.write('\n\t\tShowAudio: 0')
+               file.write('\n\t\tAudioColor: 0,1,0')
+               file.write('\n\t\tCameraOrthoZoom: 1')
+               file.write('\n\t}')
+       
+       def write_camera_default():
+               # This sucks but to match FBX converter its easier to
+               # write the cameras though they are not needed.
+               write_camera_dummy('Producer Perspective',      (0,71.3,287.5), 10, 4000, 0, (0,1,0))
+               write_camera_dummy('Producer Top',                      (0,4000,0), 1, 30000, 1, (0,0,-1))
+               write_camera_dummy('Producer Bottom',                   (0,-4000,0), 1, 30000, 1, (0,0,-1))
+               write_camera_dummy('Producer Front',                    (0,0,4000), 1, 30000, 1, (0,1,0))
+               write_camera_dummy('Producer Back',                     (0,0,-4000), 1, 30000, 1, (0,1,0))
+               write_camera_dummy('Producer Right',                    (4000,0,0), 1, 30000, 1, (0,1,0))
+               write_camera_dummy('Producer Left',                     (-4000,0,0), 1, 30000, 1, (0,1,0))
+       
+       def write_camera(my_cam):
+               '''
+               Write a blender camera
+               '''
+               render = sce.render
+               width   = render.sizeX
+               height  = render.sizeY
+               aspect  = float(width)/height
+               
+               data = my_cam.blenObject.data
+               
+               file.write('\n\tModel: "Model::%s", "Camera" {' % my_cam.fbxName )
+               file.write('\n\t\tVersion: 232')
+               loc, rot, scale, matrix, matrix_rot = write_object_props(my_cam.blenObject, None, my_cam.parRelMatrix())
+               
+               file.write('\n\t\t\tProperty: "Roll", "Roll", "A+",0')
+               file.write('\n\t\t\tProperty: "FieldOfView", "FieldOfView", "A+",%.6f' % data.angle)
+               file.write('\n\t\t\tProperty: "FieldOfViewX", "FieldOfView", "A+",1')
+               file.write('\n\t\t\tProperty: "FieldOfViewY", "FieldOfView", "A+",1')
+               file.write('\n\t\t\tProperty: "FocalLength", "Real", "A+",14.0323972702026')
+               file.write('\n\t\t\tProperty: "OpticalCenterX", "Real", "A+",%.6f' % data.shiftX) # not sure if this is in the correct units?
+               file.write('\n\t\t\tProperty: "OpticalCenterY", "Real", "A+",%.6f' % data.shiftY) # ditto 
+               file.write('\n\t\t\tProperty: "BackgroundColor", "Color", "A+",0,0,0')
+               file.write('\n\t\t\tProperty: "TurnTable", "Real", "A+",0')
+               file.write('\n\t\t\tProperty: "DisplayTurnTableIcon", "bool", "",1')
+               file.write('\n\t\t\tProperty: "Motion Blur Intensity", "Real", "A+",1')
+               file.write('\n\t\t\tProperty: "UseMotionBlur", "bool", "",0')
+               file.write('\n\t\t\tProperty: "UseRealTimeMotionBlur", "bool", "",1')
+               file.write('\n\t\t\tProperty: "ResolutionMode", "enum", "",0')
+               file.write('\n\t\t\tProperty: "ApertureMode", "enum", "",2')
+               file.write('\n\t\t\tProperty: "GateFit", "enum", "",0')
+               file.write('\n\t\t\tProperty: "CameraFormat", "enum", "",0')
+               file.write('\n\t\t\tProperty: "AspectW", "double", "",%i' % width)
+               file.write('\n\t\t\tProperty: "AspectH", "double", "",%i' % height)
+               
+               '''Camera aspect ratio modes.
+                       0 If the ratio mode is eWINDOW_SIZE, both width and height values aren't relevant.
+                       1 If the ratio mode is eFIXED_RATIO, the height value is set to 1.0 and the width value is relative to the height value.
+                       2 If the ratio mode is eFIXED_RESOLUTION, both width and height values are in pixels.
+                       3 If the ratio mode is eFIXED_WIDTH, the width value is in pixels and the height value is relative to the width value.
+                       4 If the ratio mode is eFIXED_HEIGHT, the height value is in pixels and the width value is relative to the height value. 
+               
+               Definition at line 234 of file kfbxcamera.h. '''
+               
+               file.write('\n\t\t\tProperty: "PixelAspectRatio", "double", "",2')
+               
+               file.write('\n\t\t\tProperty: "UseFrameColor", "bool", "",0')
+               file.write('\n\t\t\tProperty: "FrameColor", "ColorRGB", "",0.3,0.3,0.3')
+               file.write('\n\t\t\tProperty: "ShowName", "bool", "",1')
+               file.write('\n\t\t\tProperty: "ShowGrid", "bool", "",1')
+               file.write('\n\t\t\tProperty: "ShowOpticalCenter", "bool", "",0')
+               file.write('\n\t\t\tProperty: "ShowAzimut", "bool", "",1')
+               file.write('\n\t\t\tProperty: "ShowTimeCode", "bool", "",0')
+               file.write('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % data.clipStart)
+               file.write('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % data.clipStart)
+               file.write('\n\t\t\tProperty: "FilmWidth", "double", "",1.0')
+               file.write('\n\t\t\tProperty: "FilmHeight", "double", "",1.0')
+               file.write('\n\t\t\tProperty: "FilmAspectRatio", "double", "",%.6f' % aspect)
+               file.write('\n\t\t\tProperty: "FilmSqueezeRatio", "double", "",1')
+               file.write('\n\t\t\tProperty: "FilmFormatIndex", "enum", "",0')
+               file.write('\n\t\t\tProperty: "ViewFrustum", "bool", "",1')
+               file.write('\n\t\t\tProperty: "ViewFrustumNearFarPlane", "bool", "",0')
+               file.write('\n\t\t\tProperty: "ViewFrustumBackPlaneMode", "enum", "",2')
+               file.write('\n\t\t\tProperty: "BackPlaneDistance", "double", "",100')
+               file.write('\n\t\t\tProperty: "BackPlaneDistanceMode", "enum", "",0')
+               file.write('\n\t\t\tProperty: "ViewCameraToLookAt", "bool", "",1')
+               file.write('\n\t\t\tProperty: "LockMode", "bool", "",0')
+               file.write('\n\t\t\tProperty: "LockInterestNavigation", "bool", "",0')
+               file.write('\n\t\t\tProperty: "FitImage", "bool", "",0')
+               file.write('\n\t\t\tProperty: "Crop", "bool", "",0')
+               file.write('\n\t\t\tProperty: "Center", "bool", "",1')
+               file.write('\n\t\t\tProperty: "KeepRatio", "bool", "",1')
+               file.write('\n\t\t\tProperty: "BackgroundMode", "enum", "",0')
+               file.write('\n\t\t\tProperty: "BackgroundAlphaTreshold", "double", "",0.5')
+               file.write('\n\t\t\tProperty: "ForegroundTransparent", "bool", "",1')
+               file.write('\n\t\t\tProperty: "DisplaySafeArea", "bool", "",0')
+               file.write('\n\t\t\tProperty: "SafeAreaDisplayStyle", "enum", "",1')
+               file.write('\n\t\t\tProperty: "SafeAreaAspectRatio", "double", "",%.6f' % aspect)
+               file.write('\n\t\t\tProperty: "Use2DMagnifierZoom", "bool", "",0')
+               file.write('\n\t\t\tProperty: "2D Magnifier Zoom", "Real", "A+",100')
+               file.write('\n\t\t\tProperty: "2D Magnifier X", "Real", "A+",50')
+               file.write('\n\t\t\tProperty: "2D Magnifier Y", "Real", "A+",50')
+               file.write('\n\t\t\tProperty: "CameraProjectionType", "enum", "",0')
+               file.write('\n\t\t\tProperty: "UseRealTimeDOFAndAA", "bool", "",0')
+               file.write('\n\t\t\tProperty: "UseDepthOfField", "bool", "",0')
+               file.write('\n\t\t\tProperty: "FocusSource", "enum", "",0')
+               file.write('\n\t\t\tProperty: "FocusAngle", "double", "",3.5')
+               file.write('\n\t\t\tProperty: "FocusDistance", "double", "",200')
+               file.write('\n\t\t\tProperty: "UseAntialiasing", "bool", "",0')
+               file.write('\n\t\t\tProperty: "AntialiasingIntensity", "double", "",0.77777')
+               file.write('\n\t\t\tProperty: "UseAccumulationBuffer", "bool", "",0')
+               file.write('\n\t\t\tProperty: "FrameSamplingCount", "int", "",7')
+               
+               file.write('\n\t\t}')
+               file.write('\n\t\tMultiLayer: 0')
+               file.write('\n\t\tMultiTake: 0')
+               file.write('\n\t\tShading: Y')
+               file.write('\n\t\tCulling: "CullingOff"')
+               file.write('\n\t\tTypeFlags: "Camera"')
+               file.write('\n\t\tGeometryVersion: 124')
+               file.write('\n\t\tPosition: %.6f,%.6f,%.6f' % loc)
+               file.write('\n\t\tUp: %.6f,%.6f,%.6f' % tuple(Vector(0,1,0) * matrix_rot) )
+               file.write('\n\t\tLookAt: %.6f,%.6f,%.6f' % tuple(Vector(0,0,-1)*matrix_rot) )
+               
+               #file.write('\n\t\tUp: 0,0,0' )
+               #file.write('\n\t\tLookAt: 0,0,0' )
+               
+               file.write('\n\t\tShowInfoOnMoving: 1')
+               file.write('\n\t\tShowAudio: 0')
+               file.write('\n\t\tAudioColor: 0,1,0')
+               file.write('\n\t\tCameraOrthoZoom: 1')
+               file.write('\n\t}')
+       
+       def write_light(my_light):
+               light = my_light.blenObject.data
+               file.write('\n\tModel: "Model::%s", "Light" {' % my_light.fbxName)
+               file.write('\n\t\tVersion: 232')
+               
+               write_object_props(my_light.blenObject, None, my_light.parRelMatrix())
+               
+               # Why are these values here twice?????? - oh well, follow the holy sdk's output
+               
+               # Blender light types match FBX's, funny coincidence, we just need to
+               # be sure that all unsupported types are made into a point light
+               #ePOINT, 
+               #eDIRECTIONAL
+               #eSPOT
+               light_type = light.type
+               if light_type > 2: light_type = 1 # hemi and area lights become directional
+               
+               mode = light.mode
+               if mode & Blender.Lamp.Modes.RayShadow or mode & Blender.Lamp.Modes.Shadows:
+                       do_shadow = 1
+               else:
+                       do_shadow = 0
+               
+               if mode & Blender.Lamp.Modes.OnlyShadow or (mode & Blender.Lamp.Modes.NoDiffuse and mode & Blender.Lamp.Modes.NoSpecular):
+                       do_light = 0
+               else:
+                       do_light = 1
+               
+               scale = abs(GLOBAL_MATRIX.scalePart()[0]) # scale is always uniform in this case
+               
+               file.write('\n\t\t\tProperty: "LightType", "enum", "",%i' % light_type)
+               file.write('\n\t\t\tProperty: "CastLightOnObject", "bool", "",1')
+               file.write('\n\t\t\tProperty: "DrawVolumetricLight", "bool", "",1')
+               file.write('\n\t\t\tProperty: "DrawGroundProjection", "bool", "",1')
+               file.write('\n\t\t\tProperty: "DrawFrontFacingVolumetricLight", "bool", "",0')
+               file.write('\n\t\t\tProperty: "GoboProperty", "object", ""')
+               file.write('\n\t\t\tProperty: "Color", "Color", "A+",1,1,1')
+               file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (min(light.energy*100, 200))) # clamp below 200
+               file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale))
+               file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50')
+               file.write('\n\t\t\tProperty: "Color", "Color", "A",%.2f,%.2f,%.2f' % tuple(light.col))
+               file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (min(light.energy*100, 200))) # clamp below 200
+               file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale))
+               file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50')
+               file.write('\n\t\t\tProperty: "LightType", "enum", "",%i' % light_type)
+               file.write('\n\t\t\tProperty: "CastLightOnObject", "bool", "",%i' % do_light)
+               file.write('\n\t\t\tProperty: "DrawGroundProjection", "bool", "",1')
+               file.write('\n\t\t\tProperty: "DrawFrontFacingVolumetricLight", "bool", "",0')
+               file.write('\n\t\t\tProperty: "DrawVolumetricLight", "bool", "",1')
+               file.write('\n\t\t\tProperty: "GoboProperty", "object", ""')
+               file.write('\n\t\t\tProperty: "DecayType", "enum", "",0')
+               file.write('\n\t\t\tProperty: "DecayStart", "double", "",%.2f' % light.dist)
+               file.write('\n\t\t\tProperty: "EnableNearAttenuation", "bool", "",0')
+               file.write('\n\t\t\tProperty: "NearAttenuationStart", "double", "",0')
+               file.write('\n\t\t\tProperty: "NearAttenuationEnd", "double", "",0')
+               file.write('\n\t\t\tProperty: "EnableFarAttenuation", "bool", "",0')
+               file.write('\n\t\t\tProperty: "FarAttenuationStart", "double", "",0')
+               file.write('\n\t\t\tProperty: "FarAttenuationEnd", "double", "",0')
+               file.write('\n\t\t\tProperty: "CastShadows", "bool", "",%i' % do_shadow)
+               file.write('\n\t\t\tProperty: "ShadowColor", "ColorRGBA", "",0,0,0,1')
+               file.write('\n\t\t}')
+               file.write('\n\t\tMultiLayer: 0')
+               file.write('\n\t\tMultiTake: 0')
+               file.write('\n\t\tShading: Y')
+               file.write('\n\t\tCulling: "CullingOff"')
+               file.write('\n\t\tTypeFlags: "Light"')
+               file.write('\n\t\tGeometryVersion: 124')
+               file.write('\n\t}')
+       
+       # matrixOnly is not used at the moment
+       def write_null(my_null = None, fbxName = None, matrixOnly = None):
+               # ob can be null
+               if not fbxName: fbxName = my_null.fbxName
+               
+               file.write('\n\tModel: "Model::%s", "Null" {' % fbxName)
+               file.write('\n\t\tVersion: 232')
+               
+               # only use this for the root matrix at the moment
+               if matrixOnly:
+                       poseMatrix = write_object_props(None, None, matrixOnly)[3]
+               
+               else: # all other Null's
+                       if my_null:             poseMatrix = write_object_props(my_null.blenObject, None, my_null.parRelMatrix())[3]
+                       else:                   poseMatrix = write_object_props()[3]
+               
+               pose_items.append((fbxName, poseMatrix))
+               
+               file.write('''
+               }
+               MultiLayer: 0
+               MultiTake: 1
+               Shading: Y
+               Culling: "CullingOff"
+               TypeFlags: "Null"
+       }''')
+       
+       # Material Settings
+       if world:       world_amb = world.getAmb()
+       else:           world_amb = (0,0,0) # Default value
+       
+       def write_material(matname, mat):
+               file.write('\n\tMaterial: "Material::%s", "" {' % matname)
+               
+               # Todo, add more material Properties.
+               if mat:
+                       mat_cold = tuple(mat.rgbCol)
+                       mat_cols = tuple(mat.specCol)
+                       #mat_colm = tuple(mat.mirCol) # we wont use the mirror color
+                       mat_colamb = tuple([c for c in world_amb])
+                       
+                       mat_dif = mat.ref
+                       mat_amb = mat.amb
+                       mat_hard = (float(mat.hard)-1)/5.10
+                       mat_spec = mat.spec/2.0
+                       mat_alpha = mat.alpha
+                       mat_emit = mat.emit
+                       mat_shadeless = mat.mode & Blender.Material.Modes.SHADELESS
+                       if mat_shadeless:
+                               mat_shader = 'Lambert'
+                       else:
+                               if mat.diffuseShader == Blender.Material.Shaders.DIFFUSE_LAMBERT:
+                                       mat_shader = 'Lambert'
+                               else:
+                                       mat_shader = 'Phong'
+               else:
+                       mat_cols = mat_cold = 0.8, 0.8, 0.8
+                       mat_colamb = 0.0,0.0,0.0
+                       # mat_colm 
+                       mat_dif = 1.0
+                       mat_amb = 0.5
+                       mat_hard = 20.0
+                       mat_spec = 0.2
+                       mat_alpha = 1.0
+                       mat_emit = 0.0
+                       mat_shadeless = False
+                       mat_shader = 'Phong'
+               
+               file.write('\n\t\tVersion: 102')
+               file.write('\n\t\tShadingModel: "%s"' % mat_shader.lower())
+               file.write('\n\t\tMultiLayer: 0')
+               
+               file.write('\n\t\tProperties60:  {')
+               file.write('\n\t\t\tProperty: "ShadingModel", "KString", "", "%s"' % mat_shader)
+               file.write('\n\t\t\tProperty: "MultiLayer", "bool", "",0')
+               file.write('\n\t\t\tProperty: "EmissiveColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_cold) # emit and diffuse color are he same in blender
+               file.write('\n\t\t\tProperty: "EmissiveFactor", "double", "",%.4f' % mat_emit)
+               
+               file.write('\n\t\t\tProperty: "AmbientColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_colamb)
+               file.write('\n\t\t\tProperty: "AmbientFactor", "double", "",%.4f' % mat_amb)
+               file.write('\n\t\t\tProperty: "DiffuseColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_cold)
+               file.write('\n\t\t\tProperty: "DiffuseFactor", "double", "",%.4f' % mat_dif)
+               file.write('\n\t\t\tProperty: "Bump", "Vector3D", "",0,0,0')
+               file.write('\n\t\t\tProperty: "TransparentColor", "ColorRGB", "",1,1,1')
+               file.write('\n\t\t\tProperty: "TransparencyFactor", "double", "",%.4f' % (1.0 - mat_alpha))
+               if not mat_shadeless:
+                       file.write('\n\t\t\tProperty: "SpecularColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_cols)
+                       file.write('\n\t\t\tProperty: "SpecularFactor", "double", "",%.4f' % mat_spec)
+                       file.write('\n\t\t\tProperty: "ShininessExponent", "double", "",80.0')
+                       file.write('\n\t\t\tProperty: "ReflectionColor", "ColorRGB", "",0,0,0')
+                       file.write('\n\t\t\tProperty: "ReflectionFactor", "double", "",1')
+               file.write('\n\t\t\tProperty: "Emissive", "ColorRGB", "",0,0,0')
+               file.write('\n\t\t\tProperty: "Ambient", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_colamb)
+               file.write('\n\t\t\tProperty: "Diffuse", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cold)
+               if not mat_shadeless:
+                       file.write('\n\t\t\tProperty: "Specular", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cols)
+                       file.write('\n\t\t\tProperty: "Shininess", "double", "",%.1f' % mat_hard)
+               file.write('\n\t\t\tProperty: "Opacity", "double", "",%.1f' % mat_alpha)
+               if not mat_shadeless:
+                       file.write('\n\t\t\tProperty: "Reflectivity", "double", "",0')
+
+               file.write('\n\t\t}')
+               file.write('\n\t}')
+       
+       def write_video(texname, tex):
+               # Same as texture really!
+               file.write('\n\tVideo: "Video::%s", "Clip" {' % texname)
+               
+               file.write('''
+               Type: "Clip"
+               Properties60:  {
+                       Property: "FrameRate", "double", "",0
+                       Property: "LastFrame", "int", "",0
+                       Property: "Width", "int", "",0
+                       Property: "Height", "int", "",0''')
+               if tex:
+                       fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY)
+               else:
+                       fname = fname_strip = fname_rel = ''
+               
+               file.write('\n\t\t\tProperty: "Path", "charptr", "", "%s"' % fname_strip)
+               
+               
+               file.write('''
+                       Property: "StartFrame", "int", "",0
+                       Property: "StopFrame", "int", "",0
+                       Property: "PlaySpeed", "double", "",1
+                       Property: "Offset", "KTime", "",0
+                       Property: "InterlaceMode", "enum", "",0
+                       Property: "FreeRunning", "bool", "",0
+                       Property: "Loop", "bool", "",0
+                       Property: "AccessMode", "enum", "",0
+               }
+               UseMipMap: 0''')
+               
+               file.write('\n\t\tFilename: "%s"' % fname_strip)
+               if fname_strip: fname_strip = '/' + fname_strip
+               file.write('\n\t\tRelativeFilename: "%s"' % fname_rel) # make relative
+               file.write('\n\t}')
+
+       
+       def write_texture(texname, tex, num):
+               # if tex == None then this is a dummy tex
+               file.write('\n\tTexture: "Texture::%s", "TextureVideoClip" {' % texname)
+               file.write('\n\t\tType: "TextureVideoClip"')
+               file.write('\n\t\tVersion: 202')
+               # TODO, rare case _empty_ exists as a name.
+               file.write('\n\t\tTextureName: "Texture::%s"' % texname)
+               
+               file.write('''
+               Properties60:  {
+                       Property: "Translation", "Vector", "A+",0,0,0
+                       Property: "Rotation", "Vector", "A+",0,0,0
+                       Property: "Scaling", "Vector", "A+",1,1,1''')
+               file.write('\n\t\t\tProperty: "Texture alpha", "Number", "A+",%i' % num)
+               
+               
+               # WrapModeU/V 0==rep, 1==clamp, TODO add support
+               file.write('''
+                       Property: "TextureTypeUse", "enum", "",0
+                       Property: "CurrentTextureBlendMode", "enum", "",1
+                       Property: "UseMaterial", "bool", "",0
+                       Property: "UseMipMap", "bool", "",0
+                       Property: "CurrentMappingType", "enum", "",0
+                       Property: "UVSwap", "bool", "",0''')
+               
+               file.write('\n\t\t\tProperty: "WrapModeU", "enum", "",%i' % tex.clampX)
+               file.write('\n\t\t\tProperty: "WrapModeV", "enum", "",%i' % tex.clampY)
+               
+               file.write('''
+                       Property: "TextureRotationPivot", "Vector3D", "",0,0,0
+                       Property: "TextureScalingPivot", "Vector3D", "",0,0,0
+                       Property: "VideoProperty", "object", ""
+               }''')
+               
+               file.write('\n\t\tMedia: "Video::%s"' % texname)
+               
+               if tex:
+                       fname, fname_strip, fname_rel = derived_paths(tex.filename, basepath, EXP_IMAGE_COPY)
+               else:
+                       fname = fname_strip = fname_rel = ''
+               
+               file.write('\n\t\tFileName: "%s"' % fname_strip)
+               file.write('\n\t\tRelativeFilename: "%s"' % fname_rel) # need some make relative command
+               
+               file.write('''
+               ModelUVTranslation: 0,0
+               ModelUVScaling: 1,1
+               Texture_Alpha_Source: "None"
+               Cropping: 0,0,0,0
+       }''')
+
+       def write_deformer_skin(obname):
+               '''
+               Each mesh has its own deformer
+               '''
+               file.write('\n\tDeformer: "Deformer::Skin %s", "Skin" {' % obname)
+               file.write('''
+               Version: 100
+               MultiLayer: 0
+               Type: "Skin"
+               Properties60:  {
+               }
+               Link_DeformAcuracy: 50
+       }''')
+       
+       # in the example was 'Bip01 L Thigh_2'
+       def write_sub_deformer_skin(my_mesh, my_bone, weights):
+       
+               '''
+               Each subdeformer is spesific to a mesh, but the bone it links to can be used by many sub-deformers
+               So the SubDeformer needs the mesh-object name as a prefix to make it unique
+               
+               Its possible that there is no matching vgroup in this mesh, in that case no verts are in the subdeformer,
+               a but silly but dosnt really matter
+               '''
+               file.write('\n\tDeformer: "SubDeformer::Cluster %s %s", "Cluster" {' % (my_mesh.fbxName, my_bone.fbxName))
+               
+               file.write('''
+               Version: 100
+               MultiLayer: 0
+               Type: "Cluster"
+               Properties60:  {
+                       Property: "SrcModel", "object", ""
+                       Property: "SrcModelReference", "object", ""
+               }
+               UserData: "", ""''')
+               
+               # Support for bone parents
+               if my_mesh.fbxBoneParent:
+                       if my_mesh.fbxBoneParent == my_bone:
+                               # TODO - this is a bit lazy, we could have a simple write loop
+                               # for this case because all weights are 1.0 but for now this is ok
+                               # Parent Bones arent used all that much anyway.
+                               vgroup_data = [(j, 1.0) for j in xrange(len(my_mesh.blenData.verts))]
+                       else:
+                               # This bone is not a parent of this mesh object, no weights
+                               vgroup_data = []
+                       
+               else:
+                       # Normal weight painted mesh
+                       if my_bone.blenName in weights[0]:
+                               # Before we used normalized wright list
+                               #vgroup_data = me.getVertsFromGroup(bone.name, 1)
+                               group_index = weights[0].index(my_bone.blenName)
+                               vgroup_data = [(j, weight[group_index]) for j, weight in enumerate(weights[1]) if weight[group_index]] 
+                       else:
+                               vgroup_data = []
+               
+               file.write('\n\t\tIndexes: ')
+               
+               i = -1
+               for vg in vgroup_data:
+                       if i == -1:
+                               file.write('%i'  % vg[0])
+                               i=0
+                       else:
+                               if i==23:
+                                       file.write('\n\t\t')
+                                       i=0
+                               file.write(',%i' % vg[0])
+                       i+=1
+               
+               file.write('\n\t\tWeights: ')
+               i = -1
+               for vg in vgroup_data:
+                       if i == -1:
+                               file.write('%.8f'  % vg[1])
+                               i=0
+                       else:
+                               if i==38:
+                                       file.write('\n\t\t')
+                                       i=0
+                               file.write(',%.8f' % vg[1])
+                       i+=1
+               
+               if my_mesh.fbxParent:
+                       # TODO FIXME, this case is broken in some cases. skinned meshes just shouldnt have parents where possible!
+                       m = mtx4_z90 * (my_bone.restMatrix * my_bone.fbxArm.matrixWorld.copy() * my_mesh.matrixWorld.copy().invert() )
+               else:
+                       # Yes! this is it...  - but dosnt work when the mesh is a.
+                       m = mtx4_z90 * (my_bone.restMatrix * my_bone.fbxArm.matrixWorld.copy() * my_mesh.matrixWorld.copy().invert() )
+               
+               #m = mtx4_z90 * my_bone.restMatrix
+               matstr = mat4x4str(m)
+               matstr_i = mat4x4str(m.invert())
+               
+               file.write('\n\t\tTransform: %s' % matstr_i) # THIS IS __NOT__ THE GLOBAL MATRIX AS DOCUMENTED :/
+               file.write('\n\t\tTransformLink: %s' % matstr)
+               file.write('\n\t}')
+       
+       def write_mesh(my_mesh):
+               
+               me = my_mesh.blenData
+               
+               # if there are non NULL materials on this mesh
+               if my_mesh.blenMaterials:       do_materials = True
+               else:                                           do_materials = False
+               
+               if my_mesh.blenTextures:        do_textures = True
+               else:                                           do_textures = False     
+               
+               do_uvs = me.faceUV
+               
+               
+               file.write('\n\tModel: "Model::%s", "Mesh" {' % my_mesh.fbxName)
+               file.write('\n\t\tVersion: 232') # newline is added in write_object_props
+               
+               poseMatrix = write_object_props(my_mesh.blenObject, None, my_mesh.parRelMatrix())[3]
+               pose_items.append((my_mesh.fbxName, poseMatrix))
+               
+               file.write('\n\t\t}')
+               file.write('\n\t\tMultiLayer: 0')
+               file.write('\n\t\tMultiTake: 1')
+               file.write('\n\t\tShading: Y')
+               file.write('\n\t\tCulling: "CullingOff"')
+               
+               
+               # Write the Real Mesh data here
+               file.write('\n\t\tVertices: ')
+               i=-1
+               
+               for v in me.verts:
+                       if i==-1:
+                               file.write('%.6f,%.6f,%.6f' % tuple(v.co));     i=0
+                       else:
+                               if i==7:
+                                       file.write('\n\t\t');   i=0
+                               file.write(',%.6f,%.6f,%.6f'% tuple(v.co))
+                       i+=1
+                               
+               file.write('\n\t\tPolygonVertexIndex: ')
+               i=-1
+               for f in me.faces:
+                       fi = [v.index for v in f]
+                       # flip the last index, odd but it looks like
+                       # this is how fbx tells one face from another
+                       fi[-1] = -(fi[-1]+1)
+                       fi = tuple(fi)
+                       if i==-1:
+                               if len(f) == 3:         file.write('%i,%i,%i' % fi )
+                               else:                           file.write('%i,%i,%i,%i' % fi )
+                               i=0
+                       else:
+                               if i==13:
+                                       file.write('\n\t\t')
+                                       i=0
+                               if len(f) == 3:         file.write(',%i,%i,%i' % fi )
+                               else:                           file.write(',%i,%i,%i,%i' % fi )
+                       i+=1
+               
+               file.write('\n\t\tEdges: ')
+               i=-1
+               for ed in me.edges:
+                               if i==-1:
+                                       file.write('%i,%i' % (ed.v1.index, ed.v2.index))
+                                       i=0
+                               else:
+                                       if i==13:
+                                               file.write('\n\t\t')
+                                               i=0
+                                       file.write(',%i,%i' % (ed.v1.index, ed.v2.index))
+                               i+=1
+               
+               file.write('\n\t\tGeometryVersion: 124')
+               
+               file.write('''
+               LayerElementNormal: 0 {
+                       Version: 101
+                       Name: ""
+                       MappingInformationType: "ByVertice"
+                       ReferenceInformationType: "Direct"
+                       Normals: ''')
+               
+               i=-1
+               for v in me.verts:
+                       if i==-1:
+                               file.write('%.15f,%.15f,%.15f' % tuple(v.no));  i=0
+                       else:
+                               if i==2:
+                                       file.write('\n                   ');    i=0
+                               file.write(',%.15f,%.15f,%.15f' % tuple(v.no))
+                       i+=1
+               file.write('\n\t\t}')
+               
+               # Write Face Smoothing
+               file.write('''
+               LayerElementSmoothing: 0 {
+                       Version: 102
+                       Name: ""
+                       MappingInformationType: "ByPolygon"
+                       ReferenceInformationType: "Direct"
+                       Smoothing: ''')
+               
+               i=-1
+               for f in me.faces:
+                       if i==-1:
+                               file.write('%i' % f.smooth);    i=0
+                       else:
+                               if i==54:
+                                       file.write('\n                   ');    i=0
+                               file.write(',%i' % f.smooth)
+                       i+=1
+               
+               file.write('\n\t\t}')
+               
+               # Write Edge Smoothing
+               file.write('''
+               LayerElementSmoothing: 1 {
+                       Version: 101
+                       Name: ""
+                       MappingInformationType: "ByEdge"
+                       ReferenceInformationType: "Direct"
+                       Smoothing: ''')
+               
+               SHARP = Blender.Mesh.EdgeFlags.SHARP
+               i=-1
+               for ed in me.edges:
+                       if i==-1:
+                               file.write('%i' % ((ed.flag&SHARP)!=0));        i=0
+                       else:
+                               if i==54:
+                                       file.write('\n                   ');    i=0
+                               file.write(',%i' % ((ed.flag&SHARP)!=0))
+                       i+=1
+               
+               file.write('\n\t\t}')
+               del SHARP
+               
+               
+               # Write VertexColor Layers
+               # note, no programs seem to use this info :/
+               collayers = []
+               if me.vertexColors:
+                       collayers = me.getColorLayerNames()
+                       collayer_orig = me.activeColorLayer
+                       for colindex, collayer in enumerate(collayers):
+                               me.activeColorLayer = collayer
+                               file.write('\n\t\tLayerElementColor: %i {' % colindex)
+                               file.write('\n\t\t\tVersion: 101')
+                               file.write('\n\t\t\tName: "%s"' % collayer)
+                               
+                               file.write('''
+                       MappingInformationType: "ByPolygonVertex"
+                       ReferenceInformationType: "IndexToDirect"
+                       Colors: ''')
+                       
+                               i = -1
+                               ii = 0 # Count how many Colors we write
+                               
+                               for f in me.faces:
+                                       for col in f.col:
+                                               if i==-1:
+                                                       file.write('%.4f,%.4f,%.4f,1' % (col[0]/255.0, col[1]/255.0, col[2]/255.0))
+                                                       i=0
+                                               else:
+                                                       if i==7:
+                                                               file.write('\n\t\t\t\t')
+                                                               i=0
+                                                       file.write(',%.4f,%.4f,%.4f,1' % (col[0]/255.0, col[1]/255.0, col[2]/255.0))
+                                               i+=1
+                                               ii+=1 # One more Color
+                               
+                               file.write('\n\t\t\tColorIndex: ')
+                               i = -1
+                               for j in xrange(ii):
+                                       if i == -1:
+                                               file.write('%i' % j)
+                                               i=0
+                                       else:
+                                               if i==55:
+                                                       file.write('\n\t\t\t\t')
+                                                       i=0
+                                               file.write(',%i' % j)
+                                       i+=1
+                               
+                               file.write('\n\t\t}')
+               
+               
+               
+               # Write UV and texture layers.
+               uvlayers = []
+               if do_uvs:
+                       uvlayers = me.getUVLayerNames()
+                       uvlayer_orig = me.activeUVLayer
+                       for uvindex, uvlayer in enumerate(uvlayers):
+                               me.activeUVLayer = uvlayer
+                               file.write('\n\t\tLayerElementUV: %i {' % uvindex)
+                               file.write('\n\t\t\tVersion: 101')
+                               file.write('\n\t\t\tName: "%s"' % uvlayer)
+                               
+                               file.write('''
+                       MappingInformationType: "ByPolygonVertex"
+                       ReferenceInformationType: "IndexToDirect"
+                       UV: ''')
+                       
+                               i = -1
+                               ii = 0 # Count how many UVs we write
+                               
+                               for f in me.faces:
+                                       for uv in f.uv:
+                                               if i==-1:
+                                                       file.write('%.6f,%.6f' % tuple(uv))
+                                                       i=0
+                                               else:
+                                                       if i==7:
+                                                               file.write('\n                   ')
+                                                               i=0
+                                                       file.write(',%.6f,%.6f' % tuple(uv))
+                                               i+=1
+                                               ii+=1 # One more UV
+                               
+                               file.write('\n\t\t\tUVIndex: ')
+                               i = -1
+                               for j in xrange(ii):
+                                       if i == -1:
+                                               file.write('%i'  % j)
+                                               i=0
+                                       else:
+                                               if i==55:
+                                                       file.write('\n\t\t\t\t')
+                                                       i=0
+                                               file.write(',%i' % j)
+                                       i+=1
+                               
+                               file.write('\n\t\t}')
+                               
+                               if do_textures:
+                                       file.write('\n\t\tLayerElementTexture: %i {' % uvindex)
+                                       file.write('\n\t\t\tVersion: 101')
+                                       file.write('\n\t\t\tName: "%s"' % uvlayer)
+                                       
+                                       if len(my_mesh.blenTextures) == 1:
+                                               file.write('\n\t\t\tMappingInformationType: "AllSame"')
+                                       else:
+                                               file.write('\n\t\t\tMappingInformationType: "ByPolygon"')
+                                       
+                                       file.write('\n\t\t\tReferenceInformationType: "IndexToDirect"')
+                                       file.write('\n\t\t\tBlendMode: "Translucent"')
+                                       file.write('\n\t\t\tTextureAlpha: 1')
+                                       file.write('\n\t\t\tTextureId: ')
+                                       
+                                       if len(my_mesh.blenTextures) == 1:
+                                               file.write('0')
+                                       else:
+                                               texture_mapping_local = {None:-1}
+                                               
+                                               i = 0 # 1 for dummy
+                                               for tex in my_mesh.blenTextures:
+                                                       if tex: # None is set above
+                                                               texture_mapping_local[tex] = i
+                                                               i+=1
+                                               
+                                               i=-1
+                                               for f in me.faces:
+                                                       img_key = f.image
+                                                       
+                                                       if i==-1:
+                                                               i=0
+                                                               file.write( '%s' % texture_mapping_local[img_key])
+                                                       else:
+                                                               if i==55:
+                                                                       file.write('\n                   ')
+                                                                       i=0
+                                                               
+                                                               file.write(',%s' % texture_mapping_local[img_key])
+                                                       i+=1
+                               
+                               else:
+                                       file.write('''
+               LayerElementTexture: 0 {
+                       Version: 101
+                       Name: ""
+                       MappingInformationType: "NoMappingInformation"
+                       ReferenceInformationType: "IndexToDirect"
+                       BlendMode: "Translucent"
+                       TextureAlpha: 1
+                       TextureId: ''')
+                               file.write('\n\t\t}')
+                       
+                       me.activeUVLayer = uvlayer_orig
+                       
+               # Done with UV/textures.
+               
+               if do_materials:
+                       file.write('\n\t\tLayerElementMaterial: 0 {')
+                       file.write('\n\t\t\tVersion: 101')
+                       file.write('\n\t\t\tName: ""')
+                       
+                       if len(my_mesh.blenMaterials) == 1:
+                               file.write('\n\t\t\tMappingInformationType: "AllSame"')
+                       else:
+                               file.write('\n\t\t\tMappingInformationType: "ByPolygon"')
+                       
+                       file.write('\n\t\t\tReferenceInformationType: "IndexToDirect"')
+                       file.write('\n\t\t\tMaterials: ')
+                       
+                       if len(my_mesh.blenMaterials) == 1:
+                               file.write('0')
+                       else:
+                               # Build a material mapping for this 
+                               material_mapping_local = {} # local-mat & tex : global index.
+                               
+                               for j, mat_tex_pair in enumerate(my_mesh.blenMaterials):
+                                       material_mapping_local[mat_tex_pair] = j
+                               
+                               len_material_mapping_local = len(material_mapping_local)
+                               
+                               mats = my_mesh.blenMaterialList
+                               
+                               i=-1
+                               for f in me.faces:
+                                       try:    mat = mats[f.mat]
+                                       except:mat = None
+                                       
+                                       if do_uvs: tex = f.image # WARNING - MULTI UV LAYER IMAGES NOT SUPPORTED :/
+                                       else: tex = None
+                                       
+                                       if i==-1:
+                                               i=0
+                                               file.write( '%s' % (material_mapping_local[mat, tex])) # None for mat or tex is ok
+                                       else:
+                                               if i==55:
+                                                       file.write('\n\t\t\t\t')
+                                                       i=0
+                                               
+                                               file.write(',%s' % (material_mapping_local[mat, tex]))
+                                       i+=1
+                       
+                       file.write('\n\t\t}')
+               
+               file.write('''
+               Layer: 0 {
+                       Version: 100
+                       LayerElement:  {
+                               Type: "LayerElementNormal"
+                               TypedIndex: 0
+                       }''')
+               
+               if do_materials:
+                       file.write('''
+                       LayerElement:  {
+                               Type: "LayerElementMaterial"
+                               TypedIndex: 0
+                       }''')
+                       
+               # Always write this
+               if do_textures:
+                       file.write('''
+                       LayerElement:  {
+                               Type: "LayerElementTexture"
+                               TypedIndex: 0
+                       }''')
+               
+               if me.vertexColors:
+                       file.write('''
+                       LayerElement:  {
+                               Type: "LayerElementColor"
+                               TypedIndex: 0
+                       }''')
+               
+               if do_uvs: # same as me.faceUV
+                       file.write('''
+                       LayerElement:  {
+                               Type: "LayerElementUV"
+                               TypedIndex: 0
+                       }''')
+               
+               
+               file.write('\n\t\t}')
+               
+               if len(uvlayers) > 1:
+                       for i in xrange(1, len(uvlayers)):
+                               
+                               file.write('\n\t\tLayer: %i {' % i)
+                               file.write('\n\t\t\tVersion: 100')
+                               
+                               file.write('''
+                       LayerElement:  {
+                               Type: "LayerElementUV"''')
+                               
+                               file.write('\n\t\t\t\tTypedIndex: %i' % i)
+                               file.write('\n\t\t\t}')
+                               
+                               if do_textures:
+                                       
+                                       file.write('''
+                       LayerElement:  {
+                               Type: "LayerElementTexture"''')
+                                       
+                                       file.write('\n\t\t\t\tTypedIndex: %i' % i)
+                                       file.write('\n\t\t\t}')
+                               
+                               file.write('\n\t\t}')
+               
+               if len(collayers) > 1:
+                       # Take into account any UV layers
+                       layer_offset = 0
+                       if uvlayers: layer_offset = len(uvlayers)-1
+                       
+                       for i in xrange(layer_offset, len(collayers)+layer_offset):
+                               file.write('\n\t\tLayer: %i {' % i)
+                               file.write('\n\t\t\tVersion: 100')
+                               
+                               file.write('''
+                       LayerElement:  {
+                               Type: "LayerElementColor"''')
+                               
+                               file.write('\n\t\t\t\tTypedIndex: %i' % i)
+                               file.write('\n\t\t\t}')
+                               file.write('\n\t\t}')
+               file.write('\n\t}')
+       
+       def write_group(name):
+               file.write('\n\tGroupSelection: "GroupSelection::%s", "Default" {' % name)
+               
+               file.write('''
+               Properties60:  {
+                       Property: "MultiLayer", "bool", "",0
+                       Property: "Pickable", "bool", "",1
+                       Property: "Transformable", "bool", "",1
+                       Property: "Show", "bool", "",1
+               }
+               MultiLayer: 0
+       }''')
+       
+       
+       # add meshes here to clear because they are not used anywhere.
+       meshes_to_clear = []
+       
+       ob_meshes = []  
+       ob_lights = []
+       ob_cameras = []
+       # in fbx we export bones as children of the mesh
+       # armatures not a part of a mesh, will be added to ob_arms
+       ob_bones = [] 
+       ob_arms = []
+       ob_null = [] # emptys
+       
+       # List of types that have blender objects (not bones)
+       ob_all_typegroups = [ob_meshes, ob_lights, ob_cameras, ob_arms, ob_null]
+       
+       groups = [] # blender groups, only add ones that have objects in the selections
+       materials = {} # (mat, image) keys, should be a set()
+       textures = {} # should be a set()
+       
+       tmp_ob_type = ob_type = None # incase no objects are exported, so as not to raise an error
+       
+       # if EXP_OBS_SELECTED is false, use sceens objects
+       if not batch_objects:
+               if EXP_OBS_SELECTED:    tmp_objects = sce.objects.context
+               else:                                   tmp_objects = sce.objects
+       else:
+               tmp_objects = batch_objects
+       
+       if EXP_ARMATURE:
+               # This is needed so applying modifiers dosnt apply the armature deformation, its also needed
+               # ...so mesh objects return their rest worldspace matrix when bone-parents are exported as weighted meshes.
+               # set every armature to its rest, backup the original values so we done mess up the scene
+               ob_arms_orig_rest = [arm.restPosition for arm in bpy.data.armatures]
+               
+               for arm in bpy.data.armatures:
+                       arm.restPosition = True
+               
+               if ob_arms_orig_rest:
+                       for ob_base in bpy.data.objects:
+                               #if ob_base.type == 'Armature':
+                               ob_base.makeDisplayList()
+                                       
+                       # This causes the makeDisplayList command to effect the mesh
+                       Blender.Set('curframe', Blender.Get('curframe'))
+                       
+       
+       for ob_base in tmp_objects:
+               for ob, mtx in BPyObject.getDerivedObjects(ob_base):
+                       #for ob in [ob_base,]:
+                       tmp_ob_type = ob.type
+                       if tmp_ob_type == 'Camera':
+                               if EXP_CAMERA:
+                                       ob_cameras.append(my_object_generic(ob, mtx))
+                       elif tmp_ob_type == 'Lamp':
+                               if EXP_LAMP:
+                                       ob_lights.append(my_object_generic(ob, mtx))
+                       elif tmp_ob_type == 'Armature':
+                               if EXP_ARMATURE:
+                                       # TODO - armatures dont work in dupligroups!
+                                       if ob not in ob_arms: ob_arms.append(ob)
+                                       # ob_arms.append(ob) # replace later. was "ob_arms.append(sane_obname(ob), ob)"
+                       elif tmp_ob_type == 'Empty':
+                               if EXP_EMPTY:
+                                       ob_null.append(my_object_generic(ob, mtx))
+                       elif EXP_MESH:
+                               origData = True
+                               if tmp_ob_type != 'Mesh':
+                                       me = bpy.data.meshes.new()
+                                       try:    me.getFromObject(ob)
+                                       except: me = None
+                                       if me:
+                                               meshes_to_clear.append( me )
+                                               mats = me.materials
+                                               origData = False
+                               else:
+                                       # Mesh Type!
+                                       if EXP_MESH_APPLY_MOD:
+                                               me = bpy.data.meshes.new()
+                                               me.getFromObject(ob)
+                                               
+                                               # so we keep the vert groups
+                                               if EXP_ARMATURE:
+                                                       orig_mesh = ob.getData(mesh=1)
+                                                       if orig_mesh.getVertGroupNames():
+                                                               ob.copy().link(me)
+                                                               # If new mesh has no vgroups we can try add if verts are teh same
+                                                               if not me.getVertGroupNames(): # vgroups were not kept by the modifier
+                                                                       if len(me.verts) == len(orig_mesh.verts):
+                                                                               groupNames, vWeightDict = BPyMesh.meshWeight2Dict(orig_mesh)
+                                                                               BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict)
+                                               
+                                               # print ob, me, me.getVertGroupNames()
+                                               meshes_to_clear.append( me )
+                                               origData = False
+                                               mats = me.materials
+                                       else:
+                                               me = ob.getData(mesh=1)
+                                               mats = me.materials
+                                               
+                                               # Support object colors
+                                               tmp_colbits = ob.colbits
+                                               if tmp_colbits:
+                                                       tmp_ob_mats = ob.getMaterials(1) # 1 so we get None's too.
+                                                       for i in xrange(16):
+                                                               if tmp_colbits & (1<<i):
+                                                                       mats[i] = tmp_ob_mats[i]
+                                                       del tmp_ob_mats
+                                               del tmp_colbits
+                                                       
+                                       
+                               if me:
+                                       # This WILL modify meshes in blender if EXP_MESH_APPLY_MOD is disabled.
+                                       # so strictly this is bad. but only in rare cases would it have negative results
+                                       # say with dupliverts the objects would rotate a bit differently
+                                       if EXP_MESH_HQ_NORMALS:
+                                               BPyMesh.meshCalcNormals(me) # high quality normals nice for realtime engines.
+                                       
+                                       texture_mapping_local = {}
+                                       material_mapping_local = {}
+                                       if me.faceUV:
+                                               uvlayer_orig = me.activeUVLayer
+                                               for uvlayer in me.getUVLayerNames():
+                                                       me.activeUVLayer = uvlayer
+                                                       for f in me.faces:
+                                                               tex = f.image
+                                                               textures[tex] = texture_mapping_local[tex] = None
+                                                               
+                                                               try: mat = mats[f.mat]
+                                                               except: mat = None
+                                                               
+                                                               materials[mat, tex] = material_mapping_local[mat, tex] = None # should use sets, wait for blender 2.5
+                                                                       
+                                                       
+                                                       me.activeUVLayer = uvlayer_orig
+                                       else:
+                                               for mat in mats:
+                                                       # 2.44 use mat.lib too for uniqueness
+                                                       materials[mat, None] = material_mapping_local[mat, None] = None
+                                               else:
+                                                       materials[None, None] = None
+                                       
+                                       if EXP_ARMATURE:
+                                               armob = BPyObject.getObjectArmature(ob)
+                                               blenParentBoneName = None
+                                               
+                                               # parent bone - special case
+                                               if (not armob) and ob.parent and ob.parent.type == 'Armature' and ob.parentType == Blender.Object.ParentTypes.BONE:
+                                                       armob = ob.parent
+                                                       blenParentBoneName = ob.parentbonename
+                                               
+                                                       
+                                               if armob and armob not in ob_arms:
+                                                       ob_arms.append(armob)
+                                       
+                                       else:
+                                               blenParentBoneName = armob = None
+                                       
+                                       my_mesh = my_object_generic(ob, mtx)
+                                       my_mesh.blenData =              me
+                                       my_mesh.origData =              origData
+                                       my_mesh.blenMaterials = material_mapping_local.keys()
+                                       my_mesh.blenMaterialList = mats
+                                       my_mesh.blenTextures =  texture_mapping_local.keys()
+                                       
+                                       # if only 1 null texture then empty the list
+                                       if len(my_mesh.blenTextures) == 1 and my_mesh.blenTextures[0] == None:
+                                               my_mesh.blenTextures = []
+                                       
+                                       my_mesh.fbxArm =        armob                                   # replace with my_object_generic armature instance later
+                                       my_mesh.fbxBoneParent = blenParentBoneName      # replace with my_bone instance later
+                                       
+                                       ob_meshes.append( my_mesh )
+       
+       if EXP_ARMATURE:
+               # now we have the meshes, restore the rest arm position
+               for i, arm in enumerate(bpy.data.armatures):
+                       arm.restPosition = ob_arms_orig_rest[i]
+                       
+               if ob_arms_orig_rest:
+                       for ob_base in bpy.data.objects:
+                               if ob_base.type == 'Armature':
+                                       ob_base.makeDisplayList()
+                       # This causes the makeDisplayList command to effect the mesh
+                       Blender.Set('curframe', Blender.Get('curframe'))
+       
+       del tmp_ob_type, tmp_objects
+       
+       # now we have collected all armatures, add bones
+       for i, ob in enumerate(ob_arms):
+               
+               ob_arms[i] = my_arm = my_object_generic(ob)
+               
+               my_arm.fbxBones =               []
+               my_arm.blenData =               ob.data
+               my_arm.blenAction =             ob.action
+               my_arm.blenActionList = []
+               
+               # fbxName, blenderObject, my_bones, blenderActions
+               #ob_arms[i] = fbxArmObName, ob, arm_my_bones, (ob.action, [])
+               
+               for bone in my_arm.blenData.bones.values():
+                       my_bone = my_bone_class(bone, my_arm)
+                       my_arm.fbxBones.append( my_bone )
+                       ob_bones.append( my_bone )
+       
+       # add the meshes to the bones and replace the meshes armature with own armature class
+       #for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
+       for my_mesh in ob_meshes:
+               # Replace 
+               # ...this could be sped up with dictionary mapping but its unlikely for
+               # it ever to be a bottleneck - (would need 100+ meshes using armatures)
+               if my_mesh.fbxArm:
+                       for my_arm in ob_arms:
+                               if my_arm.blenObject == my_mesh.fbxArm:
+                                       my_mesh.fbxArm = my_arm
+                                       break
+               
+               for my_bone in ob_bones:
+                       
+                       # The mesh uses this bones armature!
+                       if my_bone.fbxArm == my_mesh.fbxArm:
+                               my_bone.blenMeshes[my_mesh.fbxName] = me
+                               
+                               
+                               # parent bone: replace bone names with our class instances
+                               # my_mesh.fbxBoneParent is None or a blender bone name initialy, replacing if the names match.
+                               if my_mesh.fbxBoneParent == my_bone.blenName:
+                                       my_mesh.fbxBoneParent = my_bone
+       
+       bone_deformer_count = 0 # count how many bones deform a mesh
+       my_bone_blenParent = None
+       for my_bone in ob_bones:
+               my_bone_blenParent = my_bone.blenBone.parent
+               if my_bone_blenParent:
+                       for my_bone_parent in ob_bones:
+                               # Note 2.45rc2 you can compare bones normally
+                               if my_bone_blenParent.name == my_bone_parent.blenName and my_bone.fbxArm == my_bone_parent.fbxArm:
+                                       my_bone.parent = my_bone_parent
+                                       break
+               
+               # Not used at the moment
+               # my_bone.calcRestMatrixLocal()
+               bone_deformer_count += len(my_bone.blenMeshes)
+       
+       del my_bone_blenParent 
+       
+       
+       # Build blenObject -> fbxObject mapping
+       # this is needed for groups as well as fbxParenting
+       bpy.data.objects.tag = False
+       tmp_obmapping = {}
+       for ob_generic in ob_all_typegroups:
+               for ob_base in ob_generic:
+                       ob_base.blenObject.tag = True
+                       tmp_obmapping[ob_base.blenObject] = ob_base
+       
+       # Build Groups from objects we export
+       for blenGroup in bpy.data.groups:
+               fbxGroupName = None
+               for ob in blenGroup.objects:
+                       if ob.tag:
+                               if fbxGroupName == None:
+                                       fbxGroupName = sane_groupname(blenGroup)
+                                       groups.append((fbxGroupName, blenGroup))
+                               
+                               tmp_obmapping[ob].fbxGroupNames.append(fbxGroupName) # also adds to the objects fbxGroupNames
+       
+       groups.sort() # not really needed
+       
+       # Assign parents using this mapping
+       for ob_generic in ob_all_typegroups:
+               for my_ob in ob_generic:
+                       parent = my_ob.blenObject.parent
+                       if parent and parent.tag: # does it exist and is it in the mapping
+                               my_ob.fbxParent = tmp_obmapping[parent]
+       
+       
+       del tmp_obmapping
+       # Finished finding groups we use
+       
+       
+       materials =     [(sane_matname(mat_tex_pair), mat_tex_pair) for mat_tex_pair in materials.iterkeys()]
+       textures =      [(sane_texname(tex), tex) for tex in textures.iterkeys()  if tex]
+       materials.sort() # sort by name
+       textures.sort()
+       
+       camera_count = 8
+       file.write('''
+
+; Object definitions
+;------------------------------------------------------------------
+
+Definitions:  {
+       Version: 100
+       Count: %i''' % (\
+               1+1+camera_count+\
+               len(ob_meshes)+\
+               len(ob_lights)+\
+               len(ob_cameras)+\
+               len(ob_arms)+\
+               len(ob_null)+\
+               len(ob_bones)+\
+               bone_deformer_count+\
+               len(materials)+\
+               (len(textures)*2))) # add 1 for the root model 1 for global settings
+       
+       del bone_deformer_count
+       
+       file.write('''
+       ObjectType: "Model" {
+               Count: %i
+       }''' % (\
+               1+camera_count+\
+               len(ob_meshes)+\
+               len(ob_lights)+\
+               len(ob_cameras)+\
+               len(ob_arms)+\
+               len(ob_null)+\
+               len(ob_bones))) # add 1 for the root model
+       
+       file.write('''
+       ObjectType: "Geometry" {
+               Count: %i
+       }''' % len(ob_meshes))
+       
+       if materials:
+               file.write('''
+       ObjectType: "Material" {
+               Count: %i
+       }''' % len(materials))
+       
+       if textures:
+               file.write('''
+       ObjectType: "Texture" {
+               Count: %i
+       }''' % len(textures)) # add 1 for an empty tex
+               file.write('''
+       ObjectType: "Video" {
+               Count: %i
+       }''' % len(textures)) # add 1 for an empty tex
+       
+       tmp = 0
+       # Add deformer nodes
+       for my_mesh in ob_meshes:
+               if my_mesh.fbxArm:
+                       tmp+=1
+       
+       # Add subdeformers
+       for my_bone in ob_bones:
+               tmp += len(my_bone.blenMeshes)
+       
+       if tmp:
+               file.write('''
+       ObjectType: "Deformer" {
+               Count: %i
+       }''' % tmp)
+       del tmp
+       
+       # we could avoid writing this possibly but for now just write it
+       
+       file.write('''
+       ObjectType: "Pose" {
+               Count: 1
+       }''')
+       
+       if groups:
+               file.write('''
+       ObjectType: "GroupSelection" {
+               Count: %i
+       }''' % len(groups))
+       
+       file.write('''
+       ObjectType: "GlobalSettings" {
+               Count: 1
+       }
+}''')
+
+       file.write('''
+
+; Object properties
+;------------------------------------------------------------------
+
+Objects:  {''')
+       
+       # To comply with other FBX FILES
+       write_camera_switch()
+       
+       # Write the null object
+       write_null(None, 'blend_root')# , GLOBAL_MATRIX) 
+       
+       for my_null in ob_null:
+               write_null(my_null)
+       
+       for my_arm in ob_arms:
+               write_null(my_arm)
+       
+       for my_cam in ob_cameras:
+               write_camera(my_cam)
+
+       for my_light in ob_lights:
+               write_light(my_light)
+       
+       for my_mesh in ob_meshes:
+               write_mesh(my_mesh)
+
+       #for bonename, bone, obname, me, armob in ob_bones:
+       for my_bone in ob_bones:
+               write_bone(my_bone)
+       
+       write_camera_default()
+       
+       for matname, (mat, tex) in materials:
+               write_material(matname, mat) # We only need to have a material per image pair, but no need to write any image info into the material (dumb fbx standard)
+       
+       # each texture uses a video, odd
+       for texname, tex in textures:
+               write_video(texname, tex)
+       i = 0
+       for texname, tex in textures:
+               write_texture(texname, tex, i)
+               i+=1
+       
+       for groupname, group in groups:
+               write_group(groupname)
+       
+       # NOTE - c4d and motionbuilder dont need normalized weights, but deep-exploration 5 does and (max?) do.
+       
+       # Write armature modifiers
+       # TODO - add another MODEL? - because of this skin definition.
+       for my_mesh in ob_meshes:
+               if my_mesh.fbxArm:
+                       write_deformer_skin(my_mesh.fbxName)
+                       
+                       # Get normalized weights for temorary use
+                       if my_mesh.fbxBoneParent:
+                               weights = None
+                       else:
+                               weights = meshNormalizedWeights(my_mesh.blenData)
+                       
+                       #for bonename, bone, obname, bone_mesh, armob in ob_bones:
+                       for my_bone in ob_bones:
+                               if me in my_bone.blenMeshes.itervalues():
+                                       write_sub_deformer_skin(my_mesh, my_bone, weights)
+       
+       # Write pose's really weired, only needed when an armature and mesh are used together
+       # each by themselves dont need pose data. for now only pose meshes and bones
+       
+       file.write('''
+       Pose: "Pose::BIND_POSES", "BindPose" {
+               Type: "BindPose"
+               Version: 100
+               Properties60:  {
+               }
+               NbPoseNodes: ''')
+       file.write(str(len(pose_items)))
+       
+
+       for fbxName, matrix in pose_items:
+               file.write('\n\t\tPoseNode:  {')
+               file.write('\n\t\t\tNode: "Model::%s"' % fbxName )
+               if matrix:              file.write('\n\t\t\tMatrix: %s' % mat4x4str(matrix))
+               else:                   file.write('\n\t\t\tMatrix: %s' % mat4x4str(mtx4_identity))
+               file.write('\n\t\t}')
+       
+       file.write('\n\t}')
+       
+       
+       # Finish Writing Objects
+       # Write global settings
+       file.write('''
+       GlobalSettings:  {
+               Version: 1000
+               Properties60:  {
+                       Property: "UpAxis", "int", "",1
+                       Property: "UpAxisSign", "int", "",1
+                       Property: "FrontAxis", "int", "",2
+                       Property: "FrontAxisSign", "int", "",1
+                       Property: "CoordAxis", "int", "",0
+                       Property: "CoordAxisSign", "int", "",1
+                       Property: "UnitScaleFactor", "double", "",100
+               }
+       }
+''')   
+       file.write('}')
+       
+       file.write('''
+
+; Object relations
+;------------------------------------------------------------------
+
+Relations:  {''')
+
+       file.write('\n\tModel: "Model::blend_root", "Null" {\n\t}')
+
+       for my_null in ob_null:
+               file.write('\n\tModel: "Model::%s", "Null" {\n\t}' % my_null.fbxName)
+
+       for my_arm in ob_arms:
+               file.write('\n\tModel: "Model::%s", "Null" {\n\t}' % my_arm.fbxName)
+
+       for my_mesh in ob_meshes:
+               file.write('\n\tModel: "Model::%s", "Mesh" {\n\t}' % my_mesh.fbxName)
+
+       # TODO - limbs can have the same name for multiple armatures, should prefix.
+       #for bonename, bone, obname, me, armob in ob_bones:
+       for my_bone in ob_bones:
+               file.write('\n\tModel: "Model::%s", "Limb" {\n\t}' % my_bone.fbxName)
+       
+       for my_cam in ob_cameras:
+               file.write('\n\tModel: "Model::%s", "Camera" {\n\t}' % my_cam.fbxName)
+       
+       for my_light in ob_lights:
+               file.write('\n\tModel: "Model::%s", "Light" {\n\t}' % my_light.fbxName)
+       
+       file.write('''
+       Model: "Model::Producer Perspective", "Camera" {
+       }
+       Model: "Model::Producer Top", "Camera" {
+       }
+       Model: "Model::Producer Bottom", "Camera" {
+       }
+       Model: "Model::Producer Front", "Camera" {
+       }
+       Model: "Model::Producer Back", "Camera" {
+       }
+       Model: "Model::Producer Right", "Camera" {
+       }
+       Model: "Model::Producer Left", "Camera" {
+       }
+       Model: "Model::Camera Switcher", "CameraSwitcher" {
+       }''')
+       
+       for matname, (mat, tex) in materials:
+               file.write('\n\tMaterial: "Material::%s", "" {\n\t}' % matname)
+
+       if textures:
+               for texname, tex in textures:
+                       file.write('\n\tTexture: "Texture::%s", "TextureVideoClip" {\n\t}' % texname)
+               for texname, tex in textures:
+                       file.write('\n\tVideo: "Video::%s", "Clip" {\n\t}' % texname)
+
+       # deformers - modifiers
+       for my_mesh in ob_meshes:
+               if my_mesh.fbxArm:
+                       file.write('\n\tDeformer: "Deformer::Skin %s", "Skin" {\n\t}' % my_mesh.fbxName)
+       
+       #for bonename, bone, obname, me, armob in ob_bones:
+       for my_bone in ob_bones:
+               for fbxMeshObName in my_bone.blenMeshes: # .keys() - fbxMeshObName
+                       # is this bone effecting a mesh?
+                       file.write('\n\tDeformer: "SubDeformer::Cluster %s %s", "Cluster" {\n\t}' % (fbxMeshObName, my_bone.fbxName))
+       
+       # This should be at the end
+       # file.write('\n\tPose: "Pose::BIND_POSES", "BindPose" {\n\t}')
+       
+       for groupname, group in groups:
+               file.write('\n\tGroupSelection: "GroupSelection::%s", "Default" {\n\t}' % groupname)
+       
+       file.write('\n}')
+       file.write('''
+
+; Object connections
+;------------------------------------------------------------------
+
+Connections:  {''')
+       
+       # NOTE - The FBX SDK dosnt care about the order but some importers DO!
+       # for instance, defining the material->mesh connection
+       # before the mesh->blend_root crashes cinema4d
+       
+
+       # write the fake root node
+       file.write('\n\tConnect: "OO", "Model::blend_root", "Model::Scene"')
+       
+       for ob_generic in ob_all_typegroups: # all blender 'Object's we support
+               for my_ob in ob_generic:
+                       if my_ob.fbxParent:
+                               file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_ob.fbxName, my_ob.fbxParent.fbxName))
+                       else:
+                               file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % my_ob.fbxName)
+       
+       if materials:
+               for my_mesh in ob_meshes:
+                       # Connect all materials to all objects, not good form but ok for now.
+                       for mat, tex in my_mesh.blenMaterials:
+                               if mat: mat_name = mat.name
+                               else:   mat_name = None
+                               
+                               if tex: tex_name = tex.name
+                               else:   tex_name = None
+                               
+                               file.write('\n\tConnect: "OO", "Material::%s", "Model::%s"' % (sane_name_mapping_mat[mat_name, tex_name], my_mesh.fbxName))
+       
+       if textures:
+               for my_mesh in ob_meshes:
+                       if my_mesh.blenTextures:
+                               # file.write('\n\tConnect: "OO", "Texture::_empty_", "Model::%s"' % my_mesh.fbxName)
+                               for tex in my_mesh.blenTextures:
+                                       if tex:
+                                               file.write('\n\tConnect: "OO", "Texture::%s", "Model::%s"' % (sane_name_mapping_tex[tex.name], my_mesh.fbxName))
+               
+               for texname, tex in textures:
+                       file.write('\n\tConnect: "OO", "Video::%s", "Texture::%s"' % (texname, texname))
+       
+       for my_mesh in ob_meshes:
+               if my_mesh.fbxArm:
+                       file.write('\n\tConnect: "OO", "Deformer::Skin %s", "Model::%s"' % (my_mesh.fbxName, my_mesh.fbxName))
+       
+       #for bonename, bone, obname, me, armob in ob_bones:
+       for my_bone in ob_bones:
+               for fbxMeshObName in my_bone.blenMeshes: # .keys()
+                       file.write('\n\tConnect: "OO", "SubDeformer::Cluster %s %s", "Deformer::Skin %s"' % (fbxMeshObName, my_bone.fbxName, fbxMeshObName))
+       
+       # limbs -> deformers
+       # for bonename, bone, obname, me, armob in ob_bones:
+       for my_bone in ob_bones:
+               for fbxMeshObName in my_bone.blenMeshes: # .keys()
+                       file.write('\n\tConnect: "OO", "Model::%s", "SubDeformer::Cluster %s %s"' % (my_bone.fbxName, fbxMeshObName, my_bone.fbxName))
+       
+       
+       #for bonename, bone, obname, me, armob in ob_bones:
+       for my_bone in ob_bones:
+               # Always parent to armature now
+               if my_bone.parent:
+                       file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_bone.fbxName, my_bone.parent.fbxName) )
+               else:
+                       # the armature object is written as an empty and all root level bones connect to it
+                       file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_bone.fbxName, my_bone.fbxArm.fbxName) )
+       
+       # groups
+       if groups:
+               for ob_generic in ob_all_typegroups:
+                       for ob_base in ob_generic:
+                               for fbxGroupName in ob_base.fbxGroupNames:
+                                       file.write('\n\tConnect: "OO", "Model::%s", "GroupSelection::%s"' % (ob_base.fbxName, fbxGroupName))
+       
+       for my_arm in ob_arms:
+               file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % my_arm.fbxName)
+       
+       file.write('\n}')
+       
+       
+       # Needed for scene footer as well as animation
+       render = sce.render
+       
+       # from the FBX sdk
+       #define KTIME_ONE_SECOND        KTime (K_LONGLONG(46186158000))
+       def fbx_time(t):
+               # 0.5 + val is the same as rounding.
+               return int(0.5 + ((t/fps) * 46186158000))
+       
+       fps = float(render.fps) 
+       start = render.sFrame
+       end =   render.eFrame
+       if end < start: start, end = end, start
+       if start==end: ANIM_ENABLE = False
+       
+       # animations for these object types
+       ob_anim_lists = ob_bones, ob_meshes, ob_null, ob_cameras, ob_lights, ob_arms
+       
+       if ANIM_ENABLE and [tmp for tmp in ob_anim_lists if tmp]:
+               
+               frame_orig = Blender.Get('curframe')
+               
+               if ANIM_OPTIMIZE:
+                       ANIM_OPTIMIZE_PRECISSION_FLOAT = 0.1 ** ANIM_OPTIMIZE_PRECISSION
+               
+               # default action, when no actions are avaioable
+               tmp_actions = [None] # None is the default action
+               blenActionDefault = None
+               action_lastcompat = None
+               
+               if ANIM_ACTION_ALL:
+                       bpy.data.actions.tag = False
+                       tmp_actions = list(bpy.data.actions)
+                       
+                       
+                       # find which actions are compatible with the armatures
+                       # blenActions is not yet initialized so do it now.
+                       tmp_act_count = 0
+                       for my_arm in ob_arms:
+                               
+                               # get the default name
+                               if not blenActionDefault:
+                                       blenActionDefault = my_arm.blenAction
+                               
+                               arm_bone_names = set([my_bone.blenName for my_bone in my_arm.fbxBones])
+                               
+                               for action in tmp_actions:
+                                       
+                                       action_chan_names = arm_bone_names.intersection( set(action.getChannelNames()) )
+                                       
+                                       if action_chan_names: # at least one channel matches.
+                                               my_arm.blenActionList.append(action)
+                                               action.tag = True
+                                               tmp_act_count += 1
+                                               
+                                               # incase there is no actions applied to armatures
+                                               action_lastcompat = action
+                       
+                       if tmp_act_count:
+                               # unlikely to ever happen but if no actions applied to armatures, just use the last compatible armature.
+                               if not blenActionDefault:
+                                       blenActionDefault = action_lastcompat
+               
+               del action_lastcompat
+               
+               file.write('''
+;Takes and animation section
+;----------------------------------------------------
+
+Takes:  {''')
+               
+               if blenActionDefault:
+                       file.write('\n\tCurrent: "%s"' % sane_takename(blenActionDefault))
+               else:
+                       file.write('\n\tCurrent: "Default Take"')
+               
+               for blenAction in tmp_actions:
+                       # we have tagged all actious that are used be selected armatures
+                       if blenAction:
+                               if blenAction.tag:
+                                       print '\taction: "%s" exporting...' % blenAction.name
+                               else:
+                                       print '\taction: "%s" has no armature using it, skipping' % blenAction.name
+                                       continue
+                       
+                       if blenAction == None:
+                               # Warning, this only accounts for tmp_actions being [None]
+                               file.write('\n\tTake: "Default Take" {')
+                               act_start =     start
+                               act_end =       end
+                       else:
+                               # use existing name
+                               if blenAction == blenActionDefault: # have we alredy got the name
+                                       file.write('\n\tTake: "%s" {' % sane_name_mapping_take[blenAction.name])
+                               else:
+                                       file.write('\n\tTake: "%s" {' % sane_takename(blenAction))
+                                       
+                               tmp = blenAction.getFrameNumbers()
+                               if tmp:
+                                       act_start =     min(tmp)
+                                       act_end =       max(tmp)
+                                       del tmp
+                               else:
+                                       # Fallback on this, theres not much else we can do? :/
+                                       # when an action has no length
+                                       act_start =     start
+                                       act_end =       end
+                               
+                               # Set the action active
+                               for my_bone in ob_arms:
+                                       if blenAction in my_bone.blenActionList:
+                                               ob.action = blenAction
+                                               # print '\t\tSetting Action!', blenAction
+                               # sce.update(1)
+                       
+                       file.write('\n\t\tFileName: "Default_Take.tak"') # ??? - not sure why this is needed
+                       file.write('\n\t\tLocalTime: %i,%i' % (fbx_time(act_start-1), fbx_time(act_end-1))) # ??? - not sure why this is needed
+                       file.write('\n\t\tReferenceTime: %i,%i' % (fbx_time(act_start-1), fbx_time(act_end-1))) # ??? - not sure why this is needed
+                       
+                       file.write('''
+
+               ;Models animation
+               ;----------------------------------------------------''')
+                       
+                       
+                       # set pose data for all bones
+                       # do this here incase the action changes
+                       '''
+                       for my_bone in ob_bones:
+                               my_bone.flushAnimData()
+                       '''
+                       i = act_start
+                       while i <= act_end:
+                               Blender.Set('curframe', i)
+                               for ob_generic in ob_anim_lists:
+                                       for my_ob in ob_generic:
+                                               #Blender.Window.RedrawAll()
+                                               if ob_generic == ob_meshes and my_ob.fbxArm:
+                                                       # We cant animate armature meshes!
+                                                       pass
+                                               else:
+                                                       my_ob.setPoseFrame(i)
+                                               
+                               i+=1
+                       
+                       
+                       #for bonename, bone, obname, me, armob in ob_bones:
+                       for ob_generic in (ob_bones, ob_meshes, ob_null, ob_cameras, ob_lights, ob_arms):
+                                       
+                               for my_ob in ob_generic:
+                                       
+                                       if ob_generic == ob_meshes and my_ob.fbxArm:
+                                               # do nothing,
+                                               pass
+                                       else:
+                                                       
+                                               file.write('\n\t\tModel: "Model::%s" {' % my_ob.fbxName) # ??? - not sure why this is needed
+                                               file.write('\n\t\t\tVersion: 1.1')
+                                               file.write('\n\t\t\tChannel: "Transform" {')
+                                               
+                                               context_bone_anim_mats = [ (my_ob.getAnimParRelMatrix(frame), my_ob.getAnimParRelMatrixRot(frame)) for frame in xrange(act_start, act_end+1) ]
+                                               
+                                               # ----------------
+                                               # ----------------
+                                               for TX_LAYER, TX_CHAN in enumerate('TRS'): # transform, rotate, scale
+                                                       
+                                                       if              TX_CHAN=='T':   context_bone_anim_vecs = [mtx[0].translationPart()      for mtx in context_bone_anim_mats]
+                                                       elif    TX_CHAN=='S':   context_bone_anim_vecs = [mtx[0].scalePart()            for mtx in context_bone_anim_mats]
+                                                       elif    TX_CHAN=='R':
+                                                               # Was....
+                                                               # elif  TX_CHAN=='R':   context_bone_anim_vecs = [mtx[1].toEuler()                      for mtx in context_bone_anim_mats]
+                                                               # 
+                                                               # ...but we need to use the previous euler for compatible conversion.
+                                                               context_bone_anim_vecs = []
+                                                               prev_eul = None
+                                                               for mtx in context_bone_anim_mats:
+                                                                       if prev_eul:    prev_eul = mtx[1].toEuler(prev_eul)
+                                                                       else:                   prev_eul = mtx[1].toEuler()
+                                                                       context_bone_anim_vecs.append(prev_eul)
+                                                       
+                                                       file.write('\n\t\t\t\tChannel: "%s" {' % TX_CHAN) # translation
+                                                       
+                                                       for i in xrange(3):
+                                                               # Loop on each axis of the bone
+                                                               file.write('\n\t\t\t\t\tChannel: "%s" {'% ('XYZ'[i])) # translation
+                                                               file.write('\n\t\t\t\t\t\tDefault: %.15f' % context_bone_anim_vecs[0][i] )
+                                                               file.write('\n\t\t\t\t\t\tKeyVer: 4005')
+                                                               
+                                                               if not ANIM_OPTIMIZE:
+                                                                       # Just write all frames, simple but in-eficient
+                                                                       file.write('\n\t\t\t\t\t\tKeyCount: %i' % (1 + act_end - act_start))
+                                                                       file.write('\n\t\t\t\t\t\tKey: ')
+                                                                       frame = act_start
+                                                                       while frame <= act_end:
+                                                                               if frame!=act_start:
+                                                                                       file.write(',')
+                                                                               
+                                                                               # Curve types are 'C,n' for constant, 'L' for linear
+                                                                               # C,n is for bezier? - linear is best for now so we can do simple keyframe removal
+                                                                               file.write('\n\t\t\t\t\t\t\t%i,%.15f,L'  % (fbx_time(frame-1), context_bone_anim_vecs[frame-act_start][i] ))
+                                                                               frame+=1
+                                                               else:
+                                                                       # remove unneeded keys, j is the frame, needed when some frames are removed.
+                                                                       context_bone_anim_keys = [ (vec[i], j) for j, vec in enumerate(context_bone_anim_vecs) ]
+                                                                       
+                                                                       # last frame to fisrt frame, missing 1 frame on either side.
+                                                                       # removeing in a backwards loop is faster
+                                                                       #for j in xrange( (act_end-act_start)-1, 0, -1 ):
+                                                                       # j = (act_end-act_start)-1
+                                                                       j = len(context_bone_anim_keys)-2
+                                                                       while j > 0 and len(context_bone_anim_keys) > 2:
+                                                                               # print j, len(context_bone_anim_keys)
+                                                                               # Is this key the same as the ones next to it?
+                                                                               
+                                                                               # co-linear horizontal...
+                                                                               if              abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j-1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT and\
+                                                                                               abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j+1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT:
+                                                                                               
+                                                                                       del context_bone_anim_keys[j]
+                                                                                       
+                                                                               else:
+                                                                                       frame_range = float(context_bone_anim_keys[j+1][1] - context_bone_anim_keys[j-1][1])
+                                                                                       frame_range_fac1 = (context_bone_anim_keys[j+1][1] - context_bone_anim_keys[j][1]) / frame_range
+                                                                                       frame_range_fac2 = 1.0 - frame_range_fac1
+                                                                                       
+                                                                                       if abs(((context_bone_anim_keys[j-1][0]*frame_range_fac1 + context_bone_anim_keys[j+1][0]*frame_range_fac2)) - context_bone_anim_keys[j][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT:
+                                                                                               del context_bone_anim_keys[j]
+                                                                                       else:
+                                                                                               j-=1
+                                                                                       
+                                                                               # keep the index below the list length
+                                                                               if j > len(context_bone_anim_keys)-2:
+                                                                                       j = len(context_bone_anim_keys)-2
+                                                                       
+                                                                       if len(context_bone_anim_keys) == 2 and context_bone_anim_keys[0][0] == context_bone_anim_keys[1][0]:
+                                                                               # This axis has no moton, its okay to skip KeyCount and Keys in this case
+                                                                               pass
+                                                                       else:
+                                                                               # We only need to write these if there is at least one 
+                                                                               file.write('\n\t\t\t\t\t\tKeyCount: %i' % len(context_bone_anim_keys))
+                                                                               file.write('\n\t\t\t\t\t\tKey: ')
+                                                                               for val, frame in context_bone_anim_keys:
+                                                                                       if frame != context_bone_anim_keys[0][1]: # not the first
+                                                                                               file.write(',')
+                                                                                       # frame is alredy one less then blenders frame
+                                                                                       file.write('\n\t\t\t\t\t\t\t%i,%.15f,L'  % (fbx_time(frame), val ))
+                                                               
+                                                               if              i==0:   file.write('\n\t\t\t\t\t\tColor: 1,0,0')
+                                                               elif    i==1:   file.write('\n\t\t\t\t\t\tColor: 0,1,0')
+                                                               elif    i==2:   file.write('\n\t\t\t\t\t\tColor: 0,0,1')
+                                                               
+                                                               file.write('\n\t\t\t\t\t}')
+                                                       file.write('\n\t\t\t\t\tLayerType: %i' % (TX_LAYER+1) )
+                                                       file.write('\n\t\t\t\t}')
+                                               
+                                               # --------------- 
+                                               
+                                               file.write('\n\t\t\t}')
+                                               file.write('\n\t\t}')
+                       
+                       # end the take
+                       file.write('\n\t}')
+                       
+                       # end action loop. set original actions 
+                       # do this after every loop incase actions effect eachother.
+                       for my_bone in ob_arms:
+                               my_bone.blenObject.action = my_bone.blenAction
+               
+               file.write('\n}')
+               
+               Blender.Set('curframe', frame_orig)
+               
+       else:
+               # no animation
+               file.write('\n;Takes and animation section')
+               file.write('\n;----------------------------------------------------')
+               file.write('\n')
+               file.write('\nTakes:  {')
+               file.write('\n\tCurrent: ""')
+               file.write('\n}')
+       
+       
+       # write meshes animation
+       #for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
+               
+       
+       # Clear mesh data Only when writing with modifiers applied
+       for me in meshes_to_clear:
+               me.verts = None
+       
+       
+       
+       # --------------------------- Footer
+       if world:
+               has_mist = world.mode & 1
+               mist_intense, mist_start, mist_end, mist_height = world.mist
+               world_hor = world.hor
+       else:
+               has_mist = mist_intense = mist_start = mist_end = mist_height = 0
+               world_hor = 0,0,0
+       
+       file.write('\n;Version 5 settings')
+       file.write('\n;------------------------------------------------------------------')
+       file.write('\n')
+       file.write('\nVersion5:  {')
+       file.write('\n\tAmbientRenderSettings:  {')
+       file.write('\n\t\tVersion: 101')
+       file.write('\n\t\tAmbientLightColor: %.1f,%.1f,%.1f,0' % tuple(world_amb))
+       file.write('\n\t}')
+       file.write('\n\tFogOptions:  {')
+       file.write('\n\t\tFlogEnable: %i' % has_mist)
+       file.write('\n\t\tFogMode: 0')
+       file.write('\n\t\tFogDensity: %.3f' % mist_intense)
+       file.write('\n\t\tFogStart: %.3f' % mist_start)
+       file.write('\n\t\tFogEnd: %.3f' % mist_end)
+       file.write('\n\t\tFogColor: %.1f,%.1f,%.1f,1' % tuple(world_hor))
+       file.write('\n\t}')
+       file.write('\n\tSettings:  {')
+       file.write('\n\t\tFrameRate: "%i"' % int(fps))
+       file.write('\n\t\tTimeFormat: 1')
+       file.write('\n\t\tSnapOnFrames: 0')
+       file.write('\n\t\tReferenceTimeIndex: -1')
+       file.write('\n\t\tTimeLineStartTime: %i' % fbx_time(start-1))
+       file.write('\n\t\tTimeLineStopTime: %i' % fbx_time(end-1))
+       file.write('\n\t}')
+       file.write('\n\tRendererSetting:  {')
+       file.write('\n\t\tDefaultCamera: "Producer Perspective"')
+       file.write('\n\t\tDefaultViewingMode: 0')
+       file.write('\n\t}')
+       file.write('\n}')
+       file.write('\n')
+       
+       # Incase sombody imports this, clean up by clearing global dicts
+       sane_name_mapping_ob.clear()
+       sane_name_mapping_mat.clear()
+       sane_name_mapping_tex.clear()
+       
+       ob_arms[:] =    []
+       ob_bones[:] =   []
+       ob_cameras[:] = []
+       ob_lights[:] =  []
+       ob_meshes[:] =  []
+       ob_null[:] =    []
+       
+       
+       # copy images if enabled
+       if EXP_IMAGE_COPY:
+               copy_images( basepath,  [ tex[1] for tex in textures if tex[1] != None ])       
+       
+       print 'export finished in %.4f sec.' % (Blender.sys.time() - start_time)
+       return True
+       
+
+# --------------------------------------------
+# UI Function - not a part of the exporter.
+# this is to seperate the user interface from the rest of the exporter.
+from Blender import Draw, Window
+EVENT_NONE = 0
+EVENT_EXIT = 1
+EVENT_REDRAW = 2
+EVENT_FILESEL = 3
+
+GLOBALS = {}
+
+# export opts
+
+def do_redraw(e,v):            GLOBALS['EVENT'] = e
+
+# toggle between these 2, only allow one on at once
+def do_obs_sel(e,v):
+       GLOBALS['EVENT'] = e
+       GLOBALS['EXP_OBS_SCENE'].val = 0
+       GLOBALS['EXP_OBS_SELECTED'].val = 1
+
+def do_obs_sce(e,v):
+       GLOBALS['EVENT'] = e
+       GLOBALS['EXP_OBS_SCENE'].val = 1
+       GLOBALS['EXP_OBS_SELECTED'].val = 0
+
+def do_obs_sce(e,v):
+       GLOBALS['EVENT'] = e
+       GLOBALS['EXP_OBS_SCENE'].val = 1
+       GLOBALS['EXP_OBS_SELECTED'].val = 0
+
+def do_batch_type_grp(e,v):
+       GLOBALS['EVENT'] = e
+       GLOBALS['BATCH_GROUP'].val = 1
+       GLOBALS['BATCH_SCENE'].val = 0
+
+def do_batch_type_sce(e,v):
+       GLOBALS['EVENT'] = e
+       GLOBALS['BATCH_GROUP'].val = 0
+       GLOBALS['BATCH_SCENE'].val = 1
+
+def do_anim_act_all(e,v):
+       GLOBALS['EVENT'] = e
+       GLOBALS['ANIM_ACTION_ALL'][0].val = 1
+       GLOBALS['ANIM_ACTION_ALL'][1].val = 0
+
+def do_anim_act_cur(e,v):
+       if GLOBALS['BATCH_ENABLE'].val and GLOBALS['BATCH_GROUP'].val:
+               Draw.PupMenu('Warning%t|Cant use this with batch export group option')
+       else:
+               GLOBALS['EVENT'] = e
+               GLOBALS['ANIM_ACTION_ALL'][0].val = 0
+               GLOBALS['ANIM_ACTION_ALL'][1].val = 1
+
+def fbx_ui_exit(e,v):
+       GLOBALS['EVENT'] = e
+
+def do_help(e,v):
+    url = 'http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_fbx'
+    print 'Trying to open web browser with documentation at this address...'
+    print '\t' + url
+    
+    try:
+        import webbrowser
+        webbrowser.open(url)
+    except:
+        Blender.Draw.PupMenu("Error%t|Opening a webbrowser requires a full python installation")
+        print '...could not open a browser window.'
+
+       
+
+# run when export is pressed
+#def fbx_ui_write(e,v):
+def fbx_ui_write(filename):
+       
+       # Dont allow overwriting files when saving normally
+       if not GLOBALS['BATCH_ENABLE'].val:
+               if not BPyMessages.Warning_SaveOver(filename):
+                       return
+       
+       GLOBALS['EVENT'] = EVENT_EXIT
+       
+       # Keep the order the same as above for simplicity
+       # the [] is a dummy arg used for objects
+       
+       Blender.Window.WaitCursor(1)
+       
+       # Make the matrix
+       GLOBAL_MATRIX = mtx4_identity
+       GLOBAL_MATRIX[0][0] = GLOBAL_MATRIX[1][1] = GLOBAL_MATRIX[2][2] = GLOBALS['_SCALE'].val
+       if GLOBALS['_XROT90'].val:      GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_x90n
+       if GLOBALS['_YROT90'].val:      GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_y90n
+       if GLOBALS['_ZROT90'].val:      GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_z90n
+       
+       ret = write(\
+               filename, None,\
+               GLOBALS['EXP_OBS_SELECTED'].val,\
+               GLOBALS['EXP_MESH'].val,\
+               GLOBALS['EXP_MESH_APPLY_MOD'].val,\
+               GLOBALS['EXP_MESH_HQ_NORMALS'].val,\
+               GLOBALS['EXP_ARMATURE'].val,\
+               GLOBALS['EXP_LAMP'].val,\
+               GLOBALS['EXP_CAMERA'].val,\
+               GLOBALS['EXP_EMPTY'].val,\
+               GLOBALS['EXP_IMAGE_COPY'].val,\
+               GLOBAL_MATRIX,\
+               GLOBALS['ANIM_ENABLE'].val,\
+               GLOBALS['ANIM_OPTIMIZE'].val,\
+               GLOBALS['ANIM_OPTIMIZE_PRECISSION'].val,\
+               GLOBALS['ANIM_ACTION_ALL'][0].val,\
+               GLOBALS['BATCH_ENABLE'].val,\
+               GLOBALS['BATCH_GROUP'].val,\
+               GLOBALS['BATCH_SCENE'].val,\
+               GLOBALS['BATCH_FILE_PREFIX'].val,\
+               GLOBALS['BATCH_OWN_DIR'].val,\
+       )
+       
+       Blender.Window.WaitCursor(0)
+       GLOBALS.clear()
+       
+       if ret == False:
+               Draw.PupMenu('Error%t|Path cannot be written to!')
+
+
+def fbx_ui():
+       # Only to center the UI
+       x,y = GLOBALS['MOUSE']
+       x-=180; y-=0 # offset... just to get it centered
+       
+       Draw.Label('Export Objects...', x+20,y+165, 200, 20)
+       
+       if not GLOBALS['BATCH_ENABLE'].val:
+               Draw.BeginAlign()
+               GLOBALS['EXP_OBS_SELECTED'] =   Draw.Toggle('Selected Objects', EVENT_REDRAW, x+20,  y+145, 160, 20, GLOBALS['EXP_OBS_SELECTED'].val,   'Export selected objects on visible layers', do_obs_sel)
+               GLOBALS['EXP_OBS_SCENE'] =              Draw.Toggle('Scene Objects',    EVENT_REDRAW, x+180,  y+145, 160, 20, GLOBALS['EXP_OBS_SCENE'].val,             'Export all objects in this scene', do_obs_sce)
+               Draw.EndAlign()
+       
+       Draw.BeginAlign()
+       GLOBALS['_SCALE'] =             Draw.Number('Scale:',   EVENT_NONE, x+20, y+120, 140, 20, GLOBALS['_SCALE'].val,        0.01, 1000.0, 'Scale all data, (Note! some imports dont support scaled armatures)')
+       GLOBALS['_XROT90'] =    Draw.Toggle('Rot X90',  EVENT_NONE, x+160, y+120, 60, 20, GLOBALS['_XROT90'].val,               'Rotate all objects 90 degrese about the X axis')
+       GLOBALS['_YROT90'] =    Draw.Toggle('Rot Y90',  EVENT_NONE, x+220, y+120, 60, 20, GLOBALS['_YROT90'].val,               'Rotate all objects 90 degrese about the Y axis')
+       GLOBALS['_ZROT90'] =    Draw.Toggle('Rot Z90',  EVENT_NONE, x+280, y+120, 60, 20, GLOBALS['_ZROT90'].val,               'Rotate all objects 90 degrese about the Z axis')
+       Draw.EndAlign()
+       
+       y -= 35
+       
+       Draw.BeginAlign()
+       GLOBALS['EXP_EMPTY'] =          Draw.Toggle('Empty',    EVENT_NONE, x+20, y+120, 60, 20, GLOBALS['EXP_EMPTY'].val,              'Export empty objects')
+       GLOBALS['EXP_CAMERA'] =         Draw.Toggle('Camera',   EVENT_NONE, x+80, y+120, 60, 20, GLOBALS['EXP_CAMERA'].val,             'Export camera objects')
+       GLOBALS['EXP_LAMP'] =           Draw.Toggle('Lamp',             EVENT_NONE, x+140, y+120, 60, 20, GLOBALS['EXP_LAMP'].val,              'Export lamp objects')
+       GLOBALS['EXP_ARMATURE'] =       Draw.Toggle('Armature', EVENT_NONE, x+200,  y+120, 60, 20, GLOBALS['EXP_ARMATURE'].val, 'Export armature objects')
+       GLOBALS['EXP_MESH'] =           Draw.Toggle('Mesh',             EVENT_REDRAW, x+260,  y+120, 80, 20, GLOBALS['EXP_MESH'].val,   'Export mesh objects', do_redraw) #, do_axis_z)
+       Draw.EndAlign()
+       
+       if GLOBALS['EXP_MESH'].val:
+               # below mesh but
+               Draw.BeginAlign()
+               GLOBALS['EXP_MESH_APPLY_MOD'] =         Draw.Toggle('Modifiers',        EVENT_NONE, x+260,  y+100, 80, 20, GLOBALS['EXP_MESH_APPLY_MOD'].val,           'Apply modifiers to mesh objects') #, do_axis_z)
+               GLOBALS['EXP_MESH_HQ_NORMALS'] =        Draw.Toggle('HQ Normals',               EVENT_NONE, x+260,  y+80, 80, 20, GLOBALS['EXP_MESH_HQ_NORMALS'].val,           'Generate high quality normals') #, do_axis_z)
+               Draw.EndAlign()
+       
+       GLOBALS['EXP_IMAGE_COPY'] =             Draw.Toggle('Copy Image Files', EVENT_NONE, x+20, y+80, 160, 20, GLOBALS['EXP_IMAGE_COPY'].val,         'Copy image files to the destination path') #, do_axis_z)
+       
+       
+       Draw.Label('Export Armature Animation...', x+20,y+45, 300, 20)
+       
+       GLOBALS['ANIM_ENABLE'] =        Draw.Toggle('Enable Animation',         EVENT_REDRAW, x+20,  y+25, 160, 20, GLOBALS['ANIM_ENABLE'].val,         'Export keyframe animation', do_redraw)
+       if GLOBALS['ANIM_ENABLE'].val:
+               Draw.BeginAlign()
+               GLOBALS['ANIM_OPTIMIZE'] =                              Draw.Toggle('Optimize Keyframes',       EVENT_REDRAW, x+20,  y+0, 160, 20, GLOBALS['ANIM_OPTIMIZE'].val,        'Remove double keyframes', do_redraw)
+               if GLOBALS['ANIM_OPTIMIZE'].val:
+                       GLOBALS['ANIM_OPTIMIZE_PRECISSION'] =   Draw.Number('Precission: ',                     EVENT_NONE, x+180,  y+0, 160, 20, GLOBALS['ANIM_OPTIMIZE_PRECISSION'].val,      1, 16, 'Tolerence for comparing double keyframes (higher for greater accuracy)')
+               Draw.EndAlign()
+               
+               Draw.BeginAlign()
+               GLOBALS['ANIM_ACTION_ALL'][1] = Draw.Toggle('Current Action',   EVENT_REDRAW, x+20, y-25, 160, 20, GLOBALS['ANIM_ACTION_ALL'][1].val,           'Use actions currently applied to the armatures (use scene start/end frame)', do_anim_act_cur)
+               GLOBALS['ANIM_ACTION_ALL'][0] =         Draw.Toggle('All Actions',      EVENT_REDRAW, x+180,y-25, 160, 20, GLOBALS['ANIM_ACTION_ALL'][0].val,           'Use all actions for armatures', do_anim_act_all)
+               Draw.EndAlign()
+       
+       
+       Draw.Label('Export Batch...', x+20,y-60, 300, 20)
+       GLOBALS['BATCH_ENABLE'] =       Draw.Toggle('Enable Batch',             EVENT_REDRAW, x+20,  y-80, 160, 20, GLOBALS['BATCH_ENABLE'].val,                'Automate exporting multiple scenes or groups to files', do_redraw)
+       
+       if GLOBALS['BATCH_ENABLE'].val:
+               Draw.BeginAlign()
+               GLOBALS['BATCH_GROUP'] =        Draw.Toggle('Group > File',     EVENT_REDRAW, x+20,  y-105, 160, 20, GLOBALS['BATCH_GROUP'].val,                'Export each group as an FBX file', do_batch_type_grp)
+               GLOBALS['BATCH_SCENE'] =        Draw.Toggle('Scene > File',     EVENT_REDRAW, x+180,  y-105, 160, 20, GLOBALS['BATCH_SCENE'].val,       'Export each scene as an FBX file', do_batch_type_sce)
+               
+               # Own dir requires OS module
+               if os:
+                       GLOBALS['BATCH_OWN_DIR'] =              Draw.Toggle('Own Dir',  EVENT_NONE, x+20,  y-125, 80, 20, GLOBALS['BATCH_OWN_DIR'].val,         'Create a dir for each exported file')
+                       GLOBALS['BATCH_FILE_PREFIX'] =  Draw.String('Prefix: ', EVENT_NONE, x+100,  y-125, 240, 20, GLOBALS['BATCH_FILE_PREFIX'].val, 64,       'Prefix each file with this name ')
+               else:
+                       GLOBALS['BATCH_FILE_PREFIX'] =  Draw.String('Prefix: ', EVENT_NONE, x+20,  y-125, 320, 20, GLOBALS['BATCH_FILE_PREFIX'].val, 64,        'Prefix each file with this name ')
+               
+               
+               Draw.EndAlign()
+
+       #y+=80
+               
+       '''
+       Draw.BeginAlign()
+       GLOBALS['FILENAME'] =   Draw.String('path: ',   EVENT_NONE, x+20,  y-170, 300, 20, GLOBALS['FILENAME'].val, 64, 'Prefix each file with this name ')
+       Draw.PushButton('..',   EVENT_FILESEL, x+320,  y-170, 20, 20,           'Select the path', do_redraw)
+       '''
+       # Until batch is added
+       #
+       
+       
+       #Draw.BeginAlign()
+       Draw.PushButton('Online Help',  EVENT_REDRAW, x+20, y-160, 100, 20,     'Open online help in a browser window', do_help)
+       Draw.PushButton('Cancel',               EVENT_EXIT, x+130, y-160, 100, 20,      'Exit the exporter', fbx_ui_exit)
+       Draw.PushButton('Export',               EVENT_FILESEL, x+240, y-160, 100, 20,   'Export the fbx file', do_redraw)
+       
+       #Draw.PushButton('Export',      EVENT_EXIT, x+180, y-160, 160, 20,      'Export the fbx file', fbx_ui_write)
+       #Draw.EndAlign()
+       
+       # exit when mouse out of the view?
+       # GLOBALS['EVENT'] = EVENT_EXIT
+
+#def write_ui(filename):
+def write_ui():
+       
+       # globals
+       GLOBALS['EVENT'] = EVENT_REDRAW
+       #GLOBALS['MOUSE'] = Window.GetMouseCoords()
+       GLOBALS['MOUSE'] = [i/2 for i in Window.GetScreenSize()]
+       GLOBALS['FILENAME'] = ''
+       '''
+       # IF called from the fileselector
+       if filename == None:
+               GLOBALS['FILENAME'] = filename # Draw.Create(Blender.sys.makename(ext='.fbx'))
+       else:
+               GLOBALS['FILENAME'].val = filename
+       '''
+       GLOBALS['EXP_OBS_SELECTED'] =                   Draw.Create(1) # dont need 2 variables but just do this for clarity
+       GLOBALS['EXP_OBS_SCENE'] =                              Draw.Create(0)
+
+       GLOBALS['EXP_MESH'] =                                   Draw.Create(1)
+       GLOBALS['EXP_MESH_APPLY_MOD'] =                 Draw.Create(1)
+       GLOBALS['EXP_MESH_HQ_NORMALS'] =                Draw.Create(0)
+       GLOBALS['EXP_ARMATURE'] =                               Draw.Create(1)
+       GLOBALS['EXP_LAMP'] =                                   Draw.Create(1)
+       GLOBALS['EXP_CAMERA'] =                                 Draw.Create(1)
+       GLOBALS['EXP_EMPTY'] =                                  Draw.Create(1)
+       GLOBALS['EXP_IMAGE_COPY'] =                             Draw.Create(0)
+       # animation opts
+       GLOBALS['ANIM_ENABLE'] =                                Draw.Create(1)
+       GLOBALS['ANIM_OPTIMIZE'] =                              Draw.Create(1)
+       GLOBALS['ANIM_OPTIMIZE_PRECISSION'] =   Draw.Create(4) # decimal places
+       GLOBALS['ANIM_ACTION_ALL'] =                    [Draw.Create(0), Draw.Create(1)] # not just the current action
+       
+       # batch export options
+       GLOBALS['BATCH_ENABLE'] =                               Draw.Create(0)
+       GLOBALS['BATCH_GROUP'] =                                Draw.Create(1) # cant have both of these enabled at once.
+       GLOBALS['BATCH_SCENE'] =                                Draw.Create(0) # see above
+       GLOBALS['BATCH_FILE_PREFIX'] =                  Draw.Create(Blender.sys.makename(ext='_').split('\\')[-1].split('/')[-1])
+       GLOBALS['BATCH_OWN_DIR'] =                              Draw.Create(0)
+       # done setting globals
+       
+       # Used by the user interface
+       GLOBALS['_SCALE'] =                                             Draw.Create(1.0)
+       GLOBALS['_XROT90'] =                                    Draw.Create(True)
+       GLOBALS['_YROT90'] =                                    Draw.Create(False)
+       GLOBALS['_ZROT90'] =                                    Draw.Create(False)
+       
+       # best not do move the cursor
+       # Window.SetMouseCoords(*[i/2 for i in Window.GetScreenSize()])
+       
+       # hack so the toggle buttons redraw. this is not nice at all
+       while GLOBALS['EVENT'] != EVENT_EXIT:
+               
+               if GLOBALS['BATCH_ENABLE'].val and GLOBALS['BATCH_GROUP'].val and GLOBALS['ANIM_ACTION_ALL'][1].val:
+                       #Draw.PupMenu("Warning%t|Cant batch export groups with 'Current Action' ")
+                       GLOBALS['ANIM_ACTION_ALL'][0].val = 1
+                       GLOBALS['ANIM_ACTION_ALL'][1].val = 0
+               
+               if GLOBALS['EVENT'] == EVENT_FILESEL:
+                       if GLOBALS['BATCH_ENABLE'].val:
+                               txt = 'Batch FBX Dir'
+                               name = Blender.sys.expandpath('//')
+                       else:
+                               txt = 'Export FBX'
+                               name = Blender.sys.makename(ext='.fbx')
+                       
+                       Blender.Window.FileSelector(fbx_ui_write, txt, name)
+                       #fbx_ui_write('/test.fbx')
+                       break
+               
+               Draw.UIBlock(fbx_ui, 0)
+       
+       
+       # GLOBALS.clear()
+#test = [write_ui]
+if __name__ == '__main__':
+       # Cant call the file selector first because of a bug in the interface that crashes it.
+       # Blender.Window.FileSelector(write_ui, 'Export FBX', Blender.sys.makename(ext='.fbx'))
+       #write('/scratch/test.fbx')
+       #write_ui('/scratch/test.fbx')
+       
+       if not set:
+               Draw.PupMenu('Error%t|A full install of python2.3 or python 2.4+ is needed to run this script.')
+       else:
+               write_ui()
diff --git a/release/scripts/io/export_obj.py b/release/scripts/io/export_obj.py
new file mode 100644 (file)
index 0000000..7dffb5d
--- /dev/null
@@ -0,0 +1,933 @@
+#!BPY
+
+"""
+Name: 'Wavefront (.obj)...'
+Blender: 249
+Group: 'Export'
+Tooltip: 'Save a Wavefront OBJ File'
+"""
+
+__author__ = "Campbell Barton, Jiri Hnidek, Paolo Ciccone"
+__url__ = ['http://wiki.blender.org/index.php/Scripts/Manual/Export/wavefront_obj', 'www.blender.org', 'blenderartists.org']
+__version__ = "1.22"
+
+__bpydoc__ = """\
+This script is an exporter to OBJ file format.
+
+Usage:
+
+Select the objects you wish to export and run this script from "File->Export" menu.
+Selecting the default options from the popup box will be good in most cases.
+All objects that can be represented as a mesh (mesh, curve, metaball, surface, text3d)
+will be exported as mesh data.
+"""
+
+
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# Script copyright (C) Campbell J Barton 2007-2009
+# - V1.22- bspline import/export added (funded by PolyDimensions GmbH)
+#
+# 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, MA  02111-1307, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+# --------------------------------------------------------------------------
+
+
+import Blender
+from Blender import Mesh, Scene, Window, sys, Image, Draw
+import BPyMesh
+import BPyObject
+import BPySys
+import BPyMessages
+
+# Returns a tuple - path,extension.
+# 'hello.obj' >  ('hello', '.obj')
+def splitExt(path):
+       dotidx = path.rfind('.')
+       if dotidx == -1:
+               return path, ''
+       else:
+               return path[:dotidx], path[dotidx:] 
+
+def fixName(name):
+       if name == None:
+               return 'None'
+       else:
+               return name.replace(' ', '_')
+
+# A Dict of Materials
+# (material.name, image.name):matname_imagename # matname_imagename has gaps removed.
+MTL_DICT = {} 
+
+def write_mtl(filename):
+       
+       world = Blender.World.GetCurrent()
+       if world:
+               worldAmb = world.getAmb()
+       else:
+               worldAmb = (0,0,0) # Default value
+       
+       file = open(filename, "w")
+       file.write('# Blender3D MTL File: %s\n' % Blender.Get('filename').split('\\')[-1].split('/')[-1])
+       file.write('# Material Count: %i\n' % len(MTL_DICT))
+       # Write material/image combinations we have used.
+       for key, (mtl_mat_name, mat, img) in MTL_DICT.iteritems():
+               
+               # Get the Blender data for the material and the image.
+               # Having an image named None will make a bug, dont do it :)
+               
+               file.write('newmtl %s\n' % mtl_mat_name) # Define a new material: matname_imgname
+               
+               if mat:
+                       file.write('Ns %.6f\n' % ((mat.getHardness()-1) * 1.9607843137254901) ) # Hardness, convert blenders 1-511 to MTL's 
+                       file.write('Ka %.6f %.6f %.6f\n' %  tuple([c*mat.amb for c in worldAmb])  ) # Ambient, uses mirror colour,
+                       file.write('Kd %.6f %.6f %.6f\n' % tuple([c*mat.ref for c in mat.rgbCol]) ) # Diffuse
+                       file.write('Ks %.6f %.6f %.6f\n' % tuple([c*mat.spec for c in mat.specCol]) ) # Specular
+                       file.write('Ni %.6f\n' % mat.IOR) # Refraction index
+                       file.write('d %.6f\n' % mat.alpha) # Alpha (obj uses 'd' for dissolve)
+                       
+                       # 0 to disable lighting, 1 for ambient & diffuse only (specular color set to black), 2 for full lighting.
+                       if mat.getMode() & Blender.Material.Modes['SHADELESS']:
+                               file.write('illum 0\n') # ignore lighting
+                       elif mat.getSpec() == 0:
+                               file.write('illum 1\n') # no specular.
+                       else:
+                               file.write('illum 2\n') # light normaly 
+               
+               else:
+                       #write a dummy material here?
+                       file.write('Ns 0\n')
+                       file.write('Ka %.6f %.6f %.6f\n' %  tuple([c for c in worldAmb])  ) # Ambient, uses mirror colour,
+                       file.write('Kd 0.8 0.8 0.8\n')
+                       file.write('Ks 0.8 0.8 0.8\n')
+                       file.write('d 1\n') # No alpha
+                       file.write('illum 2\n') # light normaly
+               
+               # Write images!
+               if img:  # We have an image on the face!
+                       file.write('map_Kd %s\n' % img.filename.split('\\')[-1].split('/')[-1]) # Diffuse mapping image                 
+               
+               elif mat: # No face image. if we havea material search for MTex image.
+                       for mtex in mat.getTextures():
+                               if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE:
+                                       try:
+                                               filename = mtex.tex.image.filename.split('\\')[-1].split('/')[-1]
+                                               file.write('map_Kd %s\n' % filename) # Diffuse mapping image
+                                               break
+                                       except:
+                                               # Texture has no image though its an image type, best ignore.
+                                               pass
+               
+               file.write('\n\n')
+       
+       file.close()
+
+def copy_file(source, dest):
+       file = open(source, 'rb')
+       data = file.read()
+       file.close()
+       
+       file = open(dest, 'wb')
+       file.write(data)
+       file.close()
+
+
+def copy_images(dest_dir):
+       if dest_dir[-1] != sys.sep:
+               dest_dir += sys.sep
+       
+       # Get unique image names
+       uniqueImages = {}
+       for matname, mat, image in MTL_DICT.itervalues(): # Only use image name
+               # Get Texface images
+               if image:
+                       uniqueImages[image] = image # Should use sets here. wait until Python 2.4 is default.
+               
+               # Get MTex images
+               if mat:
+                       for mtex in mat.getTextures():
+                               if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE:
+                                       image_tex = mtex.tex.image
+                                       if image_tex:
+                                               try:
+                                                       uniqueImages[image_tex] = image_tex
+                                               except:
+                                                       pass
+       
+       # Now copy images
+       copyCount = 0
+       
+       for bImage in uniqueImages.itervalues():
+               image_path = sys.expandpath(bImage.filename)
+               if sys.exists(image_path):
+                       # Make a name for the target path.
+                       dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1]
+                       if not sys.exists(dest_image_path): # Image isnt alredy there
+                               print '\tCopying "%s" > "%s"' % (image_path, dest_image_path)
+                               copy_file(image_path, dest_image_path)
+                               copyCount+=1
+       print '\tCopied %d images' % copyCount
+
+
+def test_nurbs_compat(ob):
+       if ob.type != 'Curve':
+               return False
+       
+       for nu in ob.data:
+               if (not nu.knotsV) and nu.type != 1: # not a surface and not bezier
+                       return True
+       
+       return False
+
+def write_nurb(file, ob, ob_mat):
+       tot_verts = 0
+       cu = ob.data
+       
+       # use negative indices
+       Vector = Blender.Mathutils.Vector
+       for nu in cu:
+               
+               if nu.type==0:          DEG_ORDER_U = 1
+               else:                           DEG_ORDER_U = nu.orderU-1  # Tested to be correct
+               
+               if nu.type==1:
+                       print "\tWarning, bezier curve:", ob.name, "only poly and nurbs curves supported"
+                       continue
+               
+               if nu.knotsV:
+                       print "\tWarning, surface:", ob.name, "only poly and nurbs curves supported"
+                       continue
+               
+               if len(nu) <= DEG_ORDER_U:
+                       print "\tWarning, orderU is lower then vert count, skipping:", ob.name
+                       continue
+               
+               pt_num = 0
+               do_closed = (nu.flagU & 1)
+               do_endpoints = (do_closed==0) and (nu.flagU & 2)
+               
+               for pt in nu:
+                       pt = Vector(pt[0], pt[1], pt[2]) * ob_mat
+                       file.write('v %.6f %.6f %.6f\n' % (pt[0], pt[1], pt[2]))
+                       pt_num += 1
+               tot_verts += pt_num
+               
+               file.write('g %s\n' % (fixName(ob.name))) # fixName(ob.getData(1)) could use the data name too
+               file.write('cstype bspline\n') # not ideal, hard coded
+               file.write('deg %d\n' % DEG_ORDER_U) # not used for curves but most files have it still
+               
+               curve_ls = [-(i+1) for i in xrange(pt_num)]
+               
+               # 'curv' keyword
+               if do_closed:
+                       if DEG_ORDER_U == 1:
+                               pt_num += 1
+                               curve_ls.append(-1)
+                       else:
+                               pt_num += DEG_ORDER_U
+                               curve_ls = curve_ls + curve_ls[0:DEG_ORDER_U]
+               
+               file.write('curv 0.0 1.0 %s\n' % (' '.join( [str(i) for i in curve_ls] ))) # Blender has no U and V values for the curve
+               
+               # 'parm' keyword
+               tot_parm = (DEG_ORDER_U + 1) + pt_num
+               tot_parm_div = float(tot_parm-1)
+               parm_ls = [(i/tot_parm_div) for i in xrange(tot_parm)]
+               
+               if do_endpoints: # end points, force param
+                       for i in xrange(DEG_ORDER_U+1):
+                               parm_ls[i] = 0.0
+                               parm_ls[-(1+i)] = 1.0
+               
+               file.write('parm u %s\n' % ' '.join( [str(i) for i in parm_ls] ))
+
+               file.write('end\n')
+       
+       return tot_verts
+
+def write(filename, objects,\
+EXPORT_TRI=False,  EXPORT_EDGES=False,  EXPORT_NORMALS=False,  EXPORT_NORMALS_HQ=False,\
+EXPORT_UV=True,  EXPORT_MTL=True,  EXPORT_COPY_IMAGES=False,\
+EXPORT_APPLY_MODIFIERS=True, EXPORT_ROTX90=True, EXPORT_BLEN_OBS=True,\
+EXPORT_GROUP_BY_OB=False,  EXPORT_GROUP_BY_MAT=False, EXPORT_KEEP_VERT_ORDER=False,\
+EXPORT_POLYGROUPS=False, EXPORT_CURVE_AS_NURBS=True):
+       '''
+       Basic write function. The context and options must be alredy set
+       This can be accessed externaly
+       eg.
+       write( 'c:\\test\\foobar.obj', Blender.Object.GetSelected() ) # Using default options.
+       '''
+       
+       def veckey3d(v):
+               return round(v.x, 6), round(v.y, 6), round(v.z, 6)
+               
+       def veckey2d(v):
+               return round(v.x, 6), round(v.y, 6)
+       
+       def findVertexGroupName(face, vWeightMap):
+               """
+               Searches the vertexDict to see what groups is assigned to a given face.
+               We use a frequency system in order to sort out the name because a given vetex can
+               belong to two or more groups at the same time. To find the right name for the face
+               we list all the possible vertex group names with their frequency and then sort by
+               frequency in descend order. The top element is the one shared by the highest number
+               of vertices is the face's group 
+               """
+               weightDict = {}
+               for vert in face:
+                       vWeights = vWeightMap[vert.index]
+                       for vGroupName, weight in vWeights:
+                               weightDict[vGroupName] = weightDict.get(vGroupName, 0) + weight
+               
+               if weightDict:
+                       alist = [(weight,vGroupName) for vGroupName, weight in weightDict.iteritems()] # sort least to greatest amount of weight
+                       alist.sort()
+                       return(alist[-1][1]) # highest value last
+               else:
+                       return '(null)'
+
+
+       print 'OBJ Export path: "%s"' % filename
+       temp_mesh_name = '~tmp-mesh'
+
+       time1 = sys.time()
+       scn = Scene.GetCurrent()
+
+       file = open(filename, "w")
+       
+       # Write Header
+       file.write('# Blender3D v%s OBJ File: %s\n' % (Blender.Get('version'), Blender.Get('filename').split('/')[-1].split('\\')[-1] ))
+       file.write('# www.blender3d.org\n')
+
+       # Tell the obj file what material file to use.
+       if EXPORT_MTL:
+               mtlfilename = '%s.mtl' % '.'.join(filename.split('.')[:-1])
+               file.write('mtllib %s\n' % ( mtlfilename.split('\\')[-1].split('/')[-1] ))
+       
+       # Get the container mesh. - used for applying modifiers and non mesh objects.
+       containerMesh = meshName = tempMesh = None
+       for meshName in Blender.NMesh.GetNames():
+               if meshName.startswith(temp_mesh_name):
+                       tempMesh = Mesh.Get(meshName)
+                       if not tempMesh.users:
+                               containerMesh = tempMesh
+       if not containerMesh:
+               containerMesh = Mesh.New(temp_mesh_name)
+       
+       if EXPORT_ROTX90:
+               mat_xrot90= Blender.Mathutils.RotationMatrix(-90, 4, 'x')
+               
+       del meshName
+       del tempMesh
+       
+       # Initialize totals, these are updated each object
+       totverts = totuvco = totno = 1
+       
+       face_vert_index = 1
+       
+       globalNormals = {}
+       
+       # Get all meshes
+       for ob_main in objects:
+               for ob, ob_mat in BPyObject.getDerivedObjects(ob_main):
+                       
+                       # Nurbs curve support
+                       if EXPORT_CURVE_AS_NURBS and test_nurbs_compat(ob):
+                               if EXPORT_ROTX90:
+                                       ob_mat = ob_mat * mat_xrot90
+                               
+                               totverts += write_nurb(file, ob, ob_mat)
+                               
+                               continue
+                       # end nurbs
+                       
+                       # Will work for non meshes now! :)
+                       # getMeshFromObject(ob, container_mesh=None, apply_modifiers=True, vgroups=True, scn=None)
+                       me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, EXPORT_POLYGROUPS, scn)
+                       if not me:
+                               continue
+                       
+                       if EXPORT_UV:
+                               faceuv= me.faceUV
+                       else:
+                               faceuv = False
+                       
+                       # We have a valid mesh
+                       if EXPORT_TRI and me.faces:
+                               # Add a dummy object to it.
+                               has_quads = False
+                               for f in me.faces:
+                                       if len(f) == 4:
+                                               has_quads = True
+                                               break
+                               
+                               if has_quads:
+                                       oldmode = Mesh.Mode()
+                                       Mesh.Mode(Mesh.SelectModes['FACE'])
+                                       
+                                       me.sel = True
+                                       tempob = scn.objects.new(me)
+                                       me.quadToTriangle(0) # more=0 shortest length
+                                       oldmode = Mesh.Mode(oldmode)
+                                       scn.objects.unlink(tempob)
+                                       
+                                       Mesh.Mode(oldmode)
+                       
+                       # Make our own list so it can be sorted to reduce context switching
+                       faces = [ f for f in me.faces ]
+                       
+                       if EXPORT_EDGES:
+                               edges = me.edges
+                       else:
+                               edges = []
+                       
+                       if not (len(faces)+len(edges)+len(me.verts)): # Make sure there is somthing to write
+                               continue # dont bother with this mesh.
+                       
+                       if EXPORT_ROTX90:
+                               me.transform(ob_mat*mat_xrot90)
+                       else:
+                               me.transform(ob_mat)
+                       
+                       # High Quality Normals
+                       if EXPORT_NORMALS and faces:
+                               if EXPORT_NORMALS_HQ:
+                                       BPyMesh.meshCalcNormals(me)
+                               else:
+                                       # transforming normals is incorrect
+                                       # when the matrix is scaled,
+                                       # better to recalculate them
+                                       me.calcNormals()
+                       
+                       # # Crash Blender
+                       #materials = me.getMaterials(1) # 1 == will return None in the list.
+                       materials = me.materials
+                       
+                       materialNames = []
+                       materialItems = materials[:]
+                       if materials:
+                               for mat in materials:
+                                       if mat: # !=None
+                                               materialNames.append(mat.name)
+                                       else:
+                                               materialNames.append(None)
+                               # Cant use LC because some materials are None.
+                               # materialNames = map(lambda mat: mat.name, materials) # Bug Blender, dosent account for null materials, still broken.  
+                       
+                       # Possible there null materials, will mess up indicies
+                       # but at least it will export, wait until Blender gets fixed.
+                       materialNames.extend((16-len(materialNames)) * [None])
+                       materialItems.extend((16-len(materialItems)) * [None])
+                       
+                       # Sort by Material, then images
+                       # so we dont over context switch in the obj file.
+                       if EXPORT_KEEP_VERT_ORDER:
+                               pass
+                       elif faceuv:
+                               try:    faces.sort(key = lambda a: (a.mat, a.image, a.smooth))
+                               except: faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth)))
+                       elif len(materials) > 1:
+                               try:    faces.sort(key = lambda a: (a.mat, a.smooth))
+                               except: faces.sort(lambda a,b: cmp((a.mat, a.smooth), (b.mat, b.smooth)))
+                       else:
+                               # no materials
+                               try:    faces.sort(key = lambda a: a.smooth)
+                               except: faces.sort(lambda a,b: cmp(a.smooth, b.smooth))
+                       
+                       # Set the default mat to no material and no image.
+                       contextMat = (0, 0) # Can never be this, so we will label a new material teh first chance we get.
+                       contextSmooth = None # Will either be true or false,  set bad to force initialization switch.
+                       
+                       if EXPORT_BLEN_OBS or EXPORT_GROUP_BY_OB:
+                               name1 = ob.name
+                               name2 = ob.getData(1)
+                               if name1 == name2:
+                                       obnamestring = fixName(name1)
+                               else:
+                                       obnamestring = '%s_%s' % (fixName(name1), fixName(name2))
+                               
+                               if EXPORT_BLEN_OBS:
+                                       file.write('o %s\n' % obnamestring) # Write Object name
+                               else: # if EXPORT_GROUP_BY_OB:
+                                       file.write('g %s\n' % obnamestring)
+                       
+                       
+                       # Vert
+                       for v in me.verts:
+                               file.write('v %.6f %.6f %.6f\n' % tuple(v.co))
+                       
+                       # UV
+                       if faceuv:
+                               uv_face_mapping = [[0,0,0,0] for f in faces] # a bit of a waste for tri's :/
+                               
+                               uv_dict = {} # could use a set() here
+                               for f_index, f in enumerate(faces):
+                                       
+                                       for uv_index, uv in enumerate(f.uv):
+                                               uvkey = veckey2d(uv)
+                                               try:
+                                                       uv_face_mapping[f_index][uv_index] = uv_dict[uvkey]
+                                               except:
+                                                       uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(uv_dict)
+                                                       file.write('vt %.6f %.6f\n' % tuple(uv))
+                               
+                               uv_unique_count = len(uv_dict)
+                               del uv, uvkey, uv_dict, f_index, uv_index
+                               # Only need uv_unique_count and uv_face_mapping
+                       
+                       # NORMAL, Smooth/Non smoothed.
+                       if EXPORT_NORMALS:
+                               for f in faces:
+                                       if f.smooth:
+                                               for v in f:
+                                                       noKey = veckey3d(v.no)
+                                                       if not globalNormals.has_key( noKey ):
+                                                               globalNormals[noKey] = totno
+                                                               totno +=1
+                                                               file.write('vn %.6f %.6f %.6f\n' % noKey)
+                                       else:
+                                               # Hard, 1 normal from the face.
+                                               noKey = veckey3d(f.no)
+                                               if not globalNormals.has_key( noKey ):
+                                                       globalNormals[noKey] = totno
+                                                       totno +=1
+                                                       file.write('vn %.6f %.6f %.6f\n' % noKey)
+                       
+                       if not faceuv:
+                               f_image = None
+                       
+                       if EXPORT_POLYGROUPS:
+                               # Retrieve the list of vertex groups
+                               vertGroupNames = me.getVertGroupNames()
+
+                               currentVGroup = ''
+                               # Create a dictionary keyed by face id and listing, for each vertex, the vertex groups it belongs to
+                               vgroupsMap = [[] for _i in xrange(len(me.verts))]
+                               for vertexGroupName in vertGroupNames:
+                                       for vIdx, vWeight in me.getVertsFromGroup(vertexGroupName, 1):
+                                               vgroupsMap[vIdx].append((vertexGroupName, vWeight))
+
+                       for f_index, f in enumerate(faces):
+                               f_v= f.v
+                               f_smooth= f.smooth
+                               f_mat = min(f.mat, len(materialNames)-1)
+                               if faceuv:
+                                       f_image = f.image
+                                       f_uv= f.uv
+                               
+                               # MAKE KEY
+                               if faceuv and f_image: # Object is always true.
+                                       key = materialNames[f_mat],  f_image.name
+                               else:
+                                       key = materialNames[f_mat],  None # No image, use None instead.
+                               
+                               # Write the vertex group
+                               if EXPORT_POLYGROUPS:
+                                       if vertGroupNames:
+                                               # find what vertext group the face belongs to
+                                               theVGroup = findVertexGroupName(f,vgroupsMap)
+                                               if      theVGroup != currentVGroup:
+                                                       currentVGroup = theVGroup
+                                                       file.write('g %s\n' % theVGroup)
+
+                               # CHECK FOR CONTEXT SWITCH
+                               if key == contextMat:
+                                       pass # Context alredy switched, dont do anything
+                               else:
+                                       if key[0] == None and key[1] == None:
+                                               # Write a null material, since we know the context has changed.
+                                               if EXPORT_GROUP_BY_MAT:
+                                                       file.write('g %s_%s\n' % (fixName(ob.name), fixName(ob.getData(1))) ) # can be mat_image or (null)
+                                               file.write('usemtl (null)\n') # mat, image
+                                               
+                                       else:
+                                               mat_data= MTL_DICT.get(key)
+                                               if not mat_data:
+                                                       # First add to global dict so we can export to mtl
+                                                       # Then write mtl
+                                                       
+                                                       # Make a new names from the mat and image name,
+                                                       # converting any spaces to underscores with fixName.
+                                                       
+                                                       # If none image dont bother adding it to the name
+                                                       if key[1] == None:
+                                                               mat_data = MTL_DICT[key] = ('%s'%fixName(key[0])), materialItems[f_mat], f_image
+                                                       else:
+                                                               mat_data = MTL_DICT[key] = ('%s_%s' % (fixName(key[0]), fixName(key[1]))), materialItems[f_mat], f_image
+                                               
+                                               if EXPORT_GROUP_BY_MAT:
+                                                       file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.getData(1)), mat_data[0]) ) # can be mat_image or (null)
+
+                                               file.write('usemtl %s\n' % mat_data[0]) # can be mat_image or (null)
+                                       
+                               contextMat = key
+                               if f_smooth != contextSmooth:
+                                       if f_smooth: # on now off
+                                               file.write('s 1\n')
+                                               contextSmooth = f_smooth
+                                       else: # was off now on
+                                               file.write('s off\n')
+                                               contextSmooth = f_smooth
+                               
+                               file.write('f')
+                               if faceuv:
+                                       if EXPORT_NORMALS:
+                                               if f_smooth: # Smoothed, use vertex normals
+                                                       for vi, v in enumerate(f_v):
+                                                               file.write( ' %d/%d/%d' % (\
+                                                                 v.index+totverts,\
+                                                                 totuvco + uv_face_mapping[f_index][vi],\
+                                                                 globalNormals[ veckey3d(v.no) ])) # vert, uv, normal
+                                                       
+                                               else: # No smoothing, face normals
+                                                       no = globalNormals[ veckey3d(f.no) ]
+                                                       for vi, v in enumerate(f_v):
+                                                               file.write( ' %d/%d/%d' % (\
+                                                                 v.index+totverts,\
+                                                                 totuvco + uv_face_mapping[f_index][vi],\
+                                                                 no)) # vert, uv, normal
+                                       
+                                       else: # No Normals
+                                               for vi, v in enumerate(f_v):
+                                                       file.write( ' %d/%d' % (\
+                                                         v.index+totverts,\
+                                                         totuvco + uv_face_mapping[f_index][vi])) # vert, uv
+                                       
+                                       face_vert_index += len(f_v)
+                               
+                               else: # No UV's
+                                       if EXPORT_NORMALS:
+                                               if f_smooth: # Smoothed, use vertex normals
+                                                       for v in f_v:
+                                                               file.write( ' %d//%d' % (\
+                                                                 v.index+totverts,\
+                                                                 globalNormals[ veckey3d(v.no) ]))
+                                               else: # No smoothing, face normals
+                                                       no = globalNormals[ veckey3d(f.no) ]
+                                                       for v in f_v:
+                                                               file.write( ' %d//%d' % (\
+                                                                 v.index+totverts,\
+                                                                 no))
+                                       else: # No Normals
+                                               for v in f_v:
+                                                       file.write( ' %d' % (\
+                                                         v.index+totverts))
+                                               
+                               file.write('\n')
+                       
+                       # Write edges.
+                       if EXPORT_EDGES:
+                               LOOSE= Mesh.EdgeFlags.LOOSE
+                               for ed in edges:
+                                       if ed.flag & LOOSE:
+                                               file.write('f %d %d\n' % (ed.v1.index+totverts, ed.v2.index+totverts))
+                               
+                       # Make the indicies global rather then per mesh
+                       totverts += len(me.verts)
+                       if faceuv:
+                               totuvco += uv_unique_count
+                       me.verts= None
+       file.close()
+       
+       
+       # Now we have all our materials, save them
+       if EXPORT_MTL:
+               write_mtl(mtlfilename)
+       if EXPORT_COPY_IMAGES:
+               dest_dir = filename
+               # Remove chars until we are just the path.
+               while dest_dir and dest_dir[-1] not in '\\/':
+                       dest_dir = dest_dir[:-1]
+               if dest_dir:
+                       copy_images(dest_dir)
+               else:
+                       print '\tError: "%s" could not be used as a base for an image path.' % filename
+       
+       print "OBJ Export time: %.2f" % (sys.time() - time1)
+       
+       
+
+def write_ui(filename):
+       
+       if not filename.lower().endswith('.obj'):
+               filename += '.obj'
+       
+       if not BPyMessages.Warning_SaveOver(filename):
+               return
+       
+       global EXPORT_APPLY_MODIFIERS, EXPORT_ROTX90, EXPORT_TRI, EXPORT_EDGES,\
+               EXPORT_NORMALS, EXPORT_NORMALS_HQ, EXPORT_UV,\
+               EXPORT_MTL, EXPORT_SEL_ONLY, EXPORT_ALL_SCENES,\
+               EXPORT_ANIMATION, EXPORT_COPY_IMAGES, EXPORT_BLEN_OBS,\
+               EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER,\
+               EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS
+       
+       EXPORT_APPLY_MODIFIERS = Draw.Create(0)
+       EXPORT_ROTX90 = Draw.Create(1)
+       EXPORT_TRI = Draw.Create(0)
+       EXPORT_EDGES = Draw.Create(1)
+       EXPORT_NORMALS = Draw.Create(0)
+       EXPORT_NORMALS_HQ = Draw.Create(0)
+       EXPORT_UV = Draw.Create(1)
+       EXPORT_MTL = Draw.Create(1)
+       EXPORT_SEL_ONLY = Draw.Create(1)
+       EXPORT_ALL_SCENES = Draw.Create(0)
+       EXPORT_ANIMATION = Draw.Create(0)
+       EXPORT_COPY_IMAGES = Draw.Create(0)
+       EXPORT_BLEN_OBS = Draw.Create(0)
+       EXPORT_GROUP_BY_OB = Draw.Create(0)
+       EXPORT_GROUP_BY_MAT = Draw.Create(0)
+       EXPORT_KEEP_VERT_ORDER = Draw.Create(1)
+       EXPORT_POLYGROUPS = Draw.Create(0)
+       EXPORT_CURVE_AS_NURBS = Draw.Create(1)
+       
+       
+       # Old UI
+       '''
+       # removed too many options are bad!
+       
+       # Get USER Options
+       pup_block = [\
+       ('Context...'),\
+       ('Selection Only', EXPORT_SEL_ONLY, 'Only export objects in visible selection. Else export whole scene.'),\
+       ('All Scenes', EXPORT_ALL_SCENES, 'Each scene as a separate OBJ file.'),\
+       ('Animation', EXPORT_ANIMATION, 'Each frame as a numbered OBJ file.'),\
+       ('Object Prefs...'),\
+       ('Apply Modifiers', EXPORT_APPLY_MODIFIERS, 'Use transformed mesh data from each object. May break vert order for morph targets.'),\
+       ('Rotate X90', EXPORT_ROTX90 , 'Rotate on export so Blenders UP is translated into OBJs UP'),\
+       ('Keep Vert Order', EXPORT_KEEP_VERT_ORDER, 'Keep vert and face order, disables some other options.'),\
+       ('Extra Data...'),\
+       ('Edges', EXPORT_EDGES, 'Edges not connected to faces.'),\
+       ('Normals', EXPORT_NORMALS, 'Export vertex normal data (Ignored on import).'),\
+       ('High Quality Normals', EXPORT_NORMALS_HQ, 'Calculate high quality normals for rendering.'),\
+       ('UVs', EXPORT_UV, 'Export texface UV coords.'),\
+       ('Materials', EXPORT_MTL, 'Write a separate MTL file with the OBJ.'),\
+       ('Copy Images', EXPORT_COPY_IMAGES, 'Copy image files to the export directory, never overwrite.'),\
+       ('Triangulate', EXPORT_TRI, 'Triangulate quads.'),\
+       ('Grouping...'),\
+       ('Objects', EXPORT_BLEN_OBS, 'Export blender objects as "OBJ objects".'),\
+       ('Object Groups', EXPORT_GROUP_BY_OB, 'Export blender objects as "OBJ Groups".'),\
+       ('Material Groups', EXPORT_GROUP_BY_MAT, 'Group by materials.'),\
+       ]
+       
+       if not Draw.PupBlock('Export...', pup_block):
+               return
+       '''
+       
+       # BEGIN ALTERNATIVE UI *******************
+       if True: 
+               
+               EVENT_NONE = 0
+               EVENT_EXIT = 1
+               EVENT_REDRAW = 2
+               EVENT_EXPORT = 3
+               
+               GLOBALS = {}
+               GLOBALS['EVENT'] = EVENT_REDRAW
+               #GLOBALS['MOUSE'] = Window.GetMouseCoords()
+               GLOBALS['MOUSE'] = [i/2 for i in Window.GetScreenSize()]
+               
+               def obj_ui_set_event(e,v):
+                       GLOBALS['EVENT'] = e
+               
+               def do_split(e,v):
+                       global EXPORT_BLEN_OBS, EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_APPLY_MODIFIERS, KEEP_VERT_ORDER, EXPORT_POLYGROUPS
+                       if EXPORT_BLEN_OBS.val or EXPORT_GROUP_BY_OB.val or EXPORT_GROUP_BY_MAT.val or EXPORT_APPLY_MODIFIERS.val:
+                               EXPORT_KEEP_VERT_ORDER.val = 0
+                       else:
+                               EXPORT_KEEP_VERT_ORDER.val = 1
+                       
+               def do_vertorder(e,v):
+                       global EXPORT_BLEN_OBS, EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_APPLY_MODIFIERS, KEEP_VERT_ORDER
+                       if EXPORT_KEEP_VERT_ORDER.val:
+                               EXPORT_BLEN_OBS.val = EXPORT_GROUP_BY_OB.val = EXPORT_GROUP_BY_MAT.val = EXPORT_APPLY_MODIFIERS.val = 0
+                       else:
+                               if not (EXPORT_BLEN_OBS.val or EXPORT_GROUP_BY_OB.val or EXPORT_GROUP_BY_MAT.val or EXPORT_APPLY_MODIFIERS.val):
+                                       EXPORT_KEEP_VERT_ORDER.val = 1
+                       
+                       
+               def do_help(e,v):
+                       url = __url__[0]
+                       print 'Trying to open web browser with documentation at this address...'
+                       print '\t' + url
+                       
+                       try:
+                               import webbrowser
+                               webbrowser.open(url)
+                       except:
+                               print '...could not open a browser window.'
+               
+               def obj_ui():
+                       ui_x, ui_y = GLOBALS['MOUSE']
+                       
+                       # Center based on overall pup size
+                       ui_x -= 165
+                       ui_y -= 140
+                       
+                       global EXPORT_APPLY_MODIFIERS, EXPORT_ROTX90, EXPORT_TRI, EXPORT_EDGES,\
+                               EXPORT_NORMALS, EXPORT_NORMALS_HQ, EXPORT_UV,\
+                               EXPORT_MTL, EXPORT_SEL_ONLY, EXPORT_ALL_SCENES,\
+                               EXPORT_ANIMATION, EXPORT_COPY_IMAGES, EXPORT_BLEN_OBS,\
+                               EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER,\
+                               EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS
+
+                       Draw.Label('Context...', ui_x+9, ui_y+239, 220, 20)
+                       Draw.BeginAlign()
+                       EXPORT_SEL_ONLY = Draw.Toggle('Selection Only', EVENT_NONE, ui_x+9, ui_y+219, 110, 20, EXPORT_SEL_ONLY.val, 'Only export objects in visible selection. Else export whole scene.')
+                       EXPORT_ALL_SCENES = Draw.Toggle('All Scenes', EVENT_NONE, ui_x+119, ui_y+219, 110, 20, EXPORT_ALL_SCENES.val, 'Each scene as a separate OBJ file.')
+                       EXPORT_ANIMATION = Draw.Toggle('Animation', EVENT_NONE, ui_x+229, ui_y+219, 110, 20, EXPORT_ANIMATION.val, 'Each frame as a numbered OBJ file.')
+                       Draw.EndAlign()
+                       
+                       
+                       Draw.Label('Output Options...', ui_x+9, ui_y+189, 220, 20)
+                       Draw.BeginAlign()
+                       EXPORT_APPLY_MODIFIERS = Draw.Toggle('Apply Modifiers', EVENT_REDRAW, ui_x+9, ui_y+170, 110, 20, EXPORT_APPLY_MODIFIERS.val, 'Use transformed mesh data from each object. May break vert order for morph targets.', do_split)
+                       EXPORT_ROTX90 = Draw.Toggle('Rotate X90', EVENT_NONE, ui_x+119, ui_y+170, 110, 20, EXPORT_ROTX90.val, 'Rotate on export so Blenders UP is translated into OBJs UP')
+                       EXPORT_COPY_IMAGES = Draw.Toggle('Copy Images', EVENT_NONE, ui_x+229, ui_y+170, 110, 20, EXPORT_COPY_IMAGES.val, 'Copy image files to the export directory, never overwrite.')
+                       Draw.EndAlign()
+                       
+                       
+                       Draw.Label('Export...', ui_x+9, ui_y+139, 220, 20)
+                       Draw.BeginAlign()
+                       EXPORT_EDGES = Draw.Toggle('Edges', EVENT_NONE, ui_x+9, ui_y+120, 50, 20, EXPORT_EDGES.val, 'Edges not connected to faces.')
+                       EXPORT_TRI = Draw.Toggle('Triangulate', EVENT_NONE, ui_x+59, ui_y+120, 70, 20, EXPORT_TRI.val, 'Triangulate quads.')
+                       Draw.EndAlign()
+                       Draw.BeginAlign()
+                       EXPORT_MTL = Draw.Toggle('Materials', EVENT_NONE, ui_x+139, ui_y+120, 70, 20, EXPORT_MTL.val, 'Write a separate MTL file with the OBJ.')
+                       EXPORT_UV = Draw.Toggle('UVs', EVENT_NONE, ui_x+209, ui_y+120, 31, 20, EXPORT_UV.val, 'Export texface UV coords.')
+                       Draw.EndAlign()
+                       Draw.BeginAlign()
+                       EXPORT_NORMALS = Draw.Toggle('Normals', EVENT_NONE, ui_x+250, ui_y+120, 59, 20, EXPORT_NORMALS.val, 'Export vertex normal data (Ignored on import).')
+                       EXPORT_NORMALS_HQ = Draw.Toggle('HQ', EVENT_NONE, ui_x+309, ui_y+120, 31, 20, EXPORT_NORMALS_HQ.val, 'Calculate high quality normals for rendering.')
+                       Draw.EndAlign()
+                       EXPORT_POLYGROUPS = Draw.Toggle('Polygroups', EVENT_REDRAW, ui_x+9, ui_y+95, 120, 20, EXPORT_POLYGROUPS.val, 'Export vertex groups as OBJ groups (one group per face approximation).')
+                       
+                       EXPORT_CURVE_AS_NURBS = Draw.Toggle('Nurbs', EVENT_NONE, ui_x+139, ui_y+95, 100, 20, EXPORT_CURVE_AS_NURBS.val, 'Export 3D nurbs curves and polylines as OBJ curves, (bezier not supported).')
+                       
+                       
+                       Draw.Label('Blender Objects as OBJ:', ui_x+9, ui_y+59, 220, 20)
+                       Draw.BeginAlign()
+                       EXPORT_BLEN_OBS = Draw.Toggle('Objects', EVENT_REDRAW, ui_x+9, ui_y+39, 60, 20, EXPORT_BLEN_OBS.val, 'Export blender objects as "OBJ objects".', do_split)
+                       EXPORT_GROUP_BY_OB = Draw.Toggle('Groups', EVENT_REDRAW, ui_x+69, ui_y+39, 60, 20, EXPORT_GROUP_BY_OB.val, 'Export blender objects as "OBJ Groups".', do_split)
+                       EXPORT_GROUP_BY_MAT = Draw.Toggle('Material Groups', EVENT_REDRAW, ui_x+129, ui_y+39, 100, 20, EXPORT_GROUP_BY_MAT.val, 'Group by materials.', do_split)
+                       Draw.EndAlign()
+                       
+                       EXPORT_KEEP_VERT_ORDER = Draw.Toggle('Keep Vert Order', EVENT_REDRAW, ui_x+239, ui_y+39, 100, 20, EXPORT_KEEP_VERT_ORDER.val, 'Keep vert and face order, disables some other options. Use for morph targets.', do_vertorder)
+                       
+                       Draw.BeginAlign()
+                       Draw.PushButton('Online Help', EVENT_REDRAW, ui_x+9, ui_y+9, 110, 20, 'Load the wiki page for this script', do_help)
+                       Draw.PushButton('Cancel', EVENT_EXIT, ui_x+119, ui_y+9, 110, 20, '', obj_ui_set_event)
+                       Draw.PushButton('Export', EVENT_EXPORT, ui_x+229, ui_y+9, 110, 20, 'Export with these settings', obj_ui_set_event)
+                       Draw.EndAlign()
+
+               
+               # hack so the toggle buttons redraw. this is not nice at all
+               while GLOBALS['EVENT'] not in (EVENT_EXIT, EVENT_EXPORT):
+                       Draw.UIBlock(obj_ui, 0)
+               
+               if GLOBALS['EVENT'] != EVENT_EXPORT:
+                       return
+               
+       # END ALTERNATIVE UI *********************
+       
+       
+       if EXPORT_KEEP_VERT_ORDER.val:
+               EXPORT_BLEN_OBS.val = False
+               EXPORT_GROUP_BY_OB.val = False
+               EXPORT_GROUP_BY_MAT.val = False
+               EXPORT_APPLY_MODIFIERS.val = False
+       
+       Window.EditMode(0)
+       Window.WaitCursor(1)
+       
+       EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val
+       EXPORT_ROTX90 = EXPORT_ROTX90.val
+       EXPORT_TRI = EXPORT_TRI.val
+       EXPORT_EDGES = EXPORT_EDGES.val
+       EXPORT_NORMALS = EXPORT_NORMALS.val
+       EXPORT_NORMALS_HQ = EXPORT_NORMALS_HQ.val
+       EXPORT_UV = EXPORT_UV.val
+       EXPORT_MTL = EXPORT_MTL.val
+       EXPORT_SEL_ONLY = EXPORT_SEL_ONLY.val
+       EXPORT_ALL_SCENES = EXPORT_ALL_SCENES.val
+       EXPORT_ANIMATION = EXPORT_ANIMATION.val
+       EXPORT_COPY_IMAGES = EXPORT_COPY_IMAGES.val
+       EXPORT_BLEN_OBS = EXPORT_BLEN_OBS.val
+       EXPORT_GROUP_BY_OB = EXPORT_GROUP_BY_OB.val
+       EXPORT_GROUP_BY_MAT = EXPORT_GROUP_BY_MAT.val
+       EXPORT_KEEP_VERT_ORDER = EXPORT_KEEP_VERT_ORDER.val
+       EXPORT_POLYGROUPS = EXPORT_POLYGROUPS.val
+       EXPORT_CURVE_AS_NURBS = EXPORT_CURVE_AS_NURBS.val
+       
+       
+       base_name, ext = splitExt(filename)
+       context_name = [base_name, '', '', ext] # basename, scene_name, framenumber, extension
+       
+       # Use the options to export the data using write()
+       # def write(filename, objects, EXPORT_EDGES=False, EXPORT_NORMALS=False, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False, EXPORT_APPLY_MODIFIERS=True):
+       orig_scene = Scene.GetCurrent()
+       if EXPORT_ALL_SCENES:
+               export_scenes = Scene.Get()
+       else:
+               export_scenes = [orig_scene]
+       
+       # Export all scenes.
+       for scn in export_scenes:
+               scn.makeCurrent() # If alredy current, this is not slow.
+               context = scn.getRenderingContext()
+               orig_frame = Blender.Get('curframe')
+               
+               if EXPORT_ALL_SCENES: # Add scene name into the context_name
+                       context_name[1] = '_%s' % BPySys.cleanName(scn.name) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied.
+               
+               # Export an animation?
+               if EXPORT_ANIMATION:
+                       scene_frames = xrange(context.startFrame(), context.endFrame()+1) # up to and including the end frame.
+               else:
+                       scene_frames = [orig_frame] # Dont export an animation.
+               
+               # Loop through all frames in the scene and export.
+               for frame in scene_frames:
+                       if EXPORT_ANIMATION: # Add frame to the filename.
+                               context_name[2] = '_%.6d' % frame
+                       
+                       Blender.Set('curframe', frame)
+                       if EXPORT_SEL_ONLY:
+                               export_objects = scn.objects.context
+                       else:   
+                               export_objects = scn.objects
+                       
+                       full_path= ''.join(context_name)
+                       
+                       # erm... bit of a problem here, this can overwrite files when exporting frames. not too bad.
+                       # EXPORT THE FILE.
+                       write(full_path, export_objects,\
+                       EXPORT_TRI, EXPORT_EDGES, EXPORT_NORMALS,\
+                       EXPORT_NORMALS_HQ, EXPORT_UV, EXPORT_MTL,\
+                       EXPORT_COPY_IMAGES, EXPORT_APPLY_MODIFIERS,\
+                       EXPORT_ROTX90, EXPORT_BLEN_OBS,\
+                       EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_KEEP_VERT_ORDER,\
+                       EXPORT_POLYGROUPS, EXPORT_CURVE_AS_NURBS)
+               
+               Blender.Set('curframe', orig_frame)
+       
+       # Restore old active scene.
+       orig_scene.makeCurrent()
+       Window.WaitCursor(0)
+
+
+if __name__ == '__main__':
+       Window.FileSelector(write_ui, 'Export Wavefront OBJ', sys.makename(ext='.obj'))
diff --git a/release/scripts/io/export_ply.py b/release/scripts/io/export_ply.py
new file mode 100644 (file)
index 0000000..46d0805
--- /dev/null
@@ -0,0 +1,228 @@
+#!BPY
+
+"""
+Name: 'Stanford PLY (*.ply)...'
+Blender: 241
+Group: 'Export'
+Tooltip: 'Export active object to Stanford PLY format'
+"""
+
+import bpy
+import Blender
+from Blender import Mesh, Scene, Window, sys, Image, Draw
+import BPyMesh
+
+__author__ = "Bruce Merry"
+__version__ = "0.93"
+__bpydoc__ = """\
+This script exports Stanford PLY files from Blender. It supports normals, 
+colours, and texture coordinates per face or per vertex.
+Only one mesh can be exported at a time.
+"""
+
+# Copyright (C) 2004, 2005: Bruce Merry, bmerry@cs.uct.ac.za
+#
+# 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, MA  02111-1307, USA.
+# Vector rounding se we can use as keys
+#
+# Updated on Aug 11, 2008 by Campbell Barton
+#    - added 'comment' prefix to comments - Needed to comply with the PLY spec.
+#
+# Updated on Jan 1, 2007 by Gabe Ghearing
+#    - fixed normals so they are correctly smooth/flat
+#    - fixed crash when the model doesn't have uv coords or vertex colors
+#    - fixed crash when the model has vertex colors but doesn't have uv coords
+#    - changed float32 to float and uint8 to uchar for compatibility
+# Errata/Notes as of Jan 1, 2007
+#    - script exports texture coords if they exist even if TexFace isn't selected (not a big deal to me)
+#    - ST(R) should probably be renamed UV(T) like in most PLY files (importer needs to be updated to take either)
+#
+# Updated on Jan 3, 2007 by Gabe Ghearing
+#    - fixed "sticky" vertex UV exporting
+#    - added pupmenu to enable/disable exporting normals, uv coords, and colors
+# Errata/Notes as of Jan 3, 2007
+#    - ST(R) coords should probably be renamed UV(T) like in most PLY files (importer needs to be updated to take either)
+#    - edges should be exported since PLY files support them
+#    - code is getting spaghettish, it should be refactored...
+#
+
+
+def rvec3d(v): return round(v[0], 6), round(v[1], 6), round(v[2], 6)
+def rvec2d(v): return round(v[0], 6), round(v[1], 6)
+
+def file_callback(filename):
+       
+       if not filename.lower().endswith('.ply'):
+               filename += '.ply'
+       
+       scn= bpy.data.scenes.active
+       ob= scn.objects.active
+       if not ob:
+               Blender.Draw.PupMenu('Error%t|Select 1 active object')
+               return
+       
+       file = open(filename, 'wb')
+       
+       EXPORT_APPLY_MODIFIERS = Draw.Create(1)
+       EXPORT_NORMALS = Draw.Create(1)
+       EXPORT_UV = Draw.Create(1)
+       EXPORT_COLORS = Draw.Create(1)
+       #EXPORT_EDGES = Draw.Create(0)
+       
+       pup_block = [\
+       ('Apply Modifiers', EXPORT_APPLY_MODIFIERS, 'Use transformed mesh data.'),\
+       ('Normals', EXPORT_NORMALS, 'Export vertex normal data.'),\
+       ('UVs', EXPORT_UV, 'Export texface UV coords.'),\
+       ('Colors', EXPORT_COLORS, 'Export vertex Colors.'),\
+       #('Edges', EXPORT_EDGES, 'Edges not connected to faces.'),\
+       ]
+       
+       if not Draw.PupBlock('Export...', pup_block):
+               return
+       
+       is_editmode = Blender.Window.EditMode()
+       if is_editmode:
+               Blender.Window.EditMode(0, '', 0)
+       
+       Window.WaitCursor(1)
+       
+       EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val
+       EXPORT_NORMALS = EXPORT_NORMALS.val
+       EXPORT_UV = EXPORT_UV.val
+       EXPORT_COLORS = EXPORT_COLORS.val
+       #EXPORT_EDGES = EXPORT_EDGES.val
+       
+       mesh = BPyMesh.getMeshFromObject(ob, None, EXPORT_APPLY_MODIFIERS, False, scn)
+       
+       if not mesh:
+               Blender.Draw.PupMenu('Error%t|Could not get mesh data from active object')
+               return
+       
+       mesh.transform(ob.matrixWorld)
+       
+       faceUV = mesh.faceUV
+       vertexUV = mesh.vertexUV
+       vertexColors = mesh.vertexColors
+       
+       if (not faceUV) and (not vertexUV):             EXPORT_UV = False
+       if not vertexColors:                                    EXPORT_COLORS = False
+       
+       if not EXPORT_UV:                                               faceUV = vertexUV = False
+       if not EXPORT_COLORS:                                   vertexColors = False
+       
+       # incase
+       color = uvcoord = uvcoord_key = normal = normal_key = None
+       
+       verts = [] # list of dictionaries
+       # vdict = {} # (index, normal, uv) -> new index
+       vdict = [{} for i in xrange(len(mesh.verts))]
+       vert_count = 0
+       for i, f in enumerate(mesh.faces):
+               smooth = f.smooth
+               if not smooth:
+                       normal = tuple(f.no)
+                       normal_key = rvec3d(normal)
+                       
+               if faceUV:                      uv = f.uv
+               if vertexColors:        col = f.col
+               for j, v in enumerate(f):
+                       if smooth:
+                               normal=         tuple(v.no)
+                               normal_key = rvec3d(normal)
+                       
+                       if faceUV:
+                               uvcoord=        uv[j][0], 1.0-uv[j][1]
+                               uvcoord_key = rvec2d(uvcoord)
+                       elif vertexUV:
+                               uvcoord=        v.uvco[0], 1.0-v.uvco[1]
+                               uvcoord_key = rvec2d(uvcoord)
+                       
+                       if vertexColors:        color=          col[j].r, col[j].g, col[j].b
+                       
+                       
+                       key = normal_key, uvcoord_key, color
+                       
+                       vdict_local = vdict[v.index]
+                       
+                       if (not vdict_local) or (not vdict_local.has_key(key)):
+                               vdict_local[key] = vert_count;
+                               verts.append( (tuple(v.co), normal, uvcoord, color) )
+                               vert_count += 1
+       
+       
+       file.write('ply\n')
+       file.write('format ascii 1.0\n')
+       file.write('comment Created by Blender3D %s - www.blender.org, source file: %s\n' % (Blender.Get('version'), Blender.Get('filename').split('/')[-1].split('\\')[-1] ))
+       
+       file.write('element vertex %d\n' % len(verts))
+       
+       file.write('property float x\n')
+       file.write('property float y\n')
+       file.write('property float z\n')
+       if EXPORT_NORMALS:
+               file.write('property float nx\n')
+               file.write('property float ny\n')
+               file.write('property float nz\n')
+       
+       if EXPORT_UV:
+               file.write('property float s\n')
+               file.write('property float t\n')
+       if EXPORT_COLORS:
+               file.write('property uchar red\n')
+               file.write('property uchar green\n')
+               file.write('property uchar blue\n')
+       
+       file.write('element face %d\n' % len(mesh.faces))
+       file.write('property list uchar uint vertex_indices\n')
+       file.write('end_header\n')
+
+       for i, v in enumerate(verts):
+               file.write('%.6f %.6f %.6f ' % v[0]) # co
+               if EXPORT_NORMALS:
+                       file.write('%.6f %.6f %.6f ' % v[1]) # no
+               
+               if EXPORT_UV:
+                       file.write('%.6f %.6f ' % v[2]) # uv
+               if EXPORT_COLORS:
+                       file.write('%u %u %u' % v[3]) # col
+               file.write('\n')
+       
+       for (i, f) in enumerate(mesh.faces):
+               file.write('%d ' % len(f))
+               smooth = f.smooth
+               if not smooth: no = rvec3d(f.no)
+               
+               if faceUV:                      uv = f.uv
+               if vertexColors:        col = f.col
+               for j, v in enumerate(f):
+                       if f.smooth:            normal=         rvec3d(v.no)
+                       else:                           normal=         no
+                       if faceUV:                      uvcoord=        rvec2d((uv[j][0], 1.0-uv[j][1]))
+                       elif vertexUV:          uvcoord=        rvec2d((v.uvco[0], 1.0-v.uvco[1]))
+                       if vertexColors:        color=          col[j].r, col[j].g, col[j].b
+                       
+                       file.write('%d ' % vdict[v.index][normal, uvcoord, color])
+                       
+               file.write('\n')
+       file.close()
+       
+       if is_editmode:
+               Blender.Window.EditMode(1, '', 0)
+
+def main():
+       Blender.Window.FileSelector(file_callback, 'PLY Export', Blender.sys.makename(ext='.ply'))
+
+if __name__=='__main__':
+       main()
diff --git a/release/scripts/io/export_x3d.py b/release/scripts/io/export_x3d.py
new file mode 100644 (file)
index 0000000..b12ff67
--- /dev/null
@@ -0,0 +1,1051 @@
+#!BPY
+""" Registration info for Blender menus:
+Name: 'X3D Extensible 3D (.x3d)...'
+Blender: 245
+Group: 'Export'
+Tooltip: 'Export selection to Extensible 3D file (.x3d)'
+"""
+
+__author__ = ("Bart", "Campbell Barton")
+__email__ = ["Bart, bart:neeneenee*de"]
+__url__ = ["Author's (Bart) homepage, http://www.neeneenee.de/vrml"]
+__version__ = "2006/01/17"
+__bpydoc__ = """\
+This script exports to X3D format.
+
+Usage:
+
+Run this script from "File->Export" menu.  A pop-up will ask whether you
+want to export only selected or all relevant objects.
+
+Known issues:<br>
+       Doesn't handle multiple materials (don't use material indices);<br>
+       Doesn't handle multiple UV textures on a single mesh (create a mesh for each texture);<br>
+       Can't get the texture array associated with material * not the UV ones;
+"""
+
+
+# $Id$
+#
+#------------------------------------------------------------------------
+# X3D exporter for blender 2.36 or above
+#
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# 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, MA  02111-1307, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+#
+
+####################################
+# Library dependancies
+####################################
+
+import Blender
+from Blender import Object, Lamp, Draw, Image, Text, sys, Mesh
+from Blender.Scene import Render
+import math
+import BPyObject
+import BPyMesh
+
+# 
+DEG2RAD=0.017453292519943295
+MATWORLD= Blender.Mathutils.RotationMatrix(-90, 4, 'x')
+
+####################################
+# Global Variables
+####################################
+
+filename = Blender.Get('filename')
+_safeOverwrite = True
+
+extension = ''
+
+##########################################################
+# Functions for writing output file
+##########################################################
+
+class x3d_class:
+
+       def __init__(self, filename):
+               #--- public you can change these ---
+               self.writingcolor = 0
+               self.writingtexture = 0
+               self.writingcoords = 0
+               self.proto = 1
+               self.matonly = 0
+               self.share = 0
+               self.billnode = 0
+               self.halonode = 0
+               self.collnode = 0
+               self.tilenode = 0
+               self.verbose=2   # level of verbosity in console 0-none, 1-some, 2-most
+               self.cp=3                 # decimals for material color values   0.000 - 1.000
+               self.vp=3                 # decimals for vertex coordinate values  0.000 - n.000
+               self.tp=3                 # decimals for texture coordinate values 0.000 - 1.000
+               self.it=3
+               
+               #--- class private don't touch ---
+               self.texNames={}   # dictionary of textureNames
+               self.matNames={}   # dictionary of materiaNames
+               self.meshNames={}   # dictionary of meshNames
+               self.indentLevel=0 # keeps track of current indenting
+               self.filename=filename
+               self.file = None
+               if filename.lower().endswith('.x3dz'):
+                       try:
+                               import gzip
+                               self.file = gzip.open(filename, "w")                            
+                       except:
+                               print "failed to import compression modules, exporting uncompressed"
+                               self.filename = filename[:-1] # remove trailing z
+               
+               if self.file == None:
+                       self.file = open(self.filename, "w")
+
+               self.bNav=0
+               self.nodeID=0
+               self.namesReserved=[ "Anchor","Appearance","Arc2D","ArcClose2D","AudioClip","Background","Billboard",
+                                                        "BooleanFilter","BooleanSequencer","BooleanToggle","BooleanTrigger","Box","Circle2D",
+                                                        "Collision","Color","ColorInterpolator","ColorRGBA","component","Cone","connect",
+                                                        "Contour2D","ContourPolyline2D","Coordinate","CoordinateDouble","CoordinateInterpolator",
+                                                        "CoordinateInterpolator2D","Cylinder","CylinderSensor","DirectionalLight","Disk2D",
+                                                        "ElevationGrid","EspduTransform","EXPORT","ExternProtoDeclare","Extrusion","field",
+                                                        "fieldValue","FillProperties","Fog","FontStyle","GeoCoordinate","GeoElevationGrid",
+                                                        "GeoLocationLocation","GeoLOD","GeoMetadata","GeoOrigin","GeoPositionInterpolator",
+                                                        "GeoTouchSensor","GeoViewpoint","Group","HAnimDisplacer","HAnimHumanoid","HAnimJoint",
+                                                        "HAnimSegment","HAnimSite","head","ImageTexture","IMPORT","IndexedFaceSet",
+                                                        "IndexedLineSet","IndexedTriangleFanSet","IndexedTriangleSet","IndexedTriangleStripSet",
+                                                        "Inline","IntegerSequencer","IntegerTrigger","IS","KeySensor","LineProperties","LineSet",
+                                                        "LoadSensor","LOD","Material","meta","MetadataDouble","MetadataFloat","MetadataInteger",
+                                                        "MetadataSet","MetadataString","MovieTexture","MultiTexture","MultiTextureCoordinate",
+                                                        "MultiTextureTransform","NavigationInfo","Normal","NormalInterpolator","NurbsCurve",
+                                                        "NurbsCurve2D","NurbsOrientationInterpolator","NurbsPatchSurface",
+                                                        "NurbsPositionInterpolator","NurbsSet","NurbsSurfaceInterpolator","NurbsSweptSurface",
+                                                        "NurbsSwungSurface","NurbsTextureCoordinate","NurbsTrimmedSurface","OrientationInterpolator",
+                                                        "PixelTexture","PlaneSensor","PointLight","PointSet","Polyline2D","Polypoint2D",
+                                                        "PositionInterpolator","PositionInterpolator2D","ProtoBody","ProtoDeclare","ProtoInstance",
+                                                        "ProtoInterface","ProximitySensor","ReceiverPdu","Rectangle2D","ROUTE","ScalarInterpolator",
+                                                        "Scene","Script","Shape","SignalPdu","Sound","Sphere","SphereSensor","SpotLight","StaticGroup",
+                                                        "StringSensor","Switch","Text","TextureBackground","TextureCoordinate","TextureCoordinateGenerator",
+                                                        "TextureTransform","TimeSensor","TimeTrigger","TouchSensor","Transform","TransmitterPdu",
+                                                        "TriangleFanSet","TriangleSet","TriangleSet2D","TriangleStripSet","Viewpoint","VisibilitySensor",
+                                                        "WorldInfo","X3D","XvlShell","VertexShader","FragmentShader","MultiShaderAppearance","ShaderAppearance" ]
+               self.namesStandard=[ "Empty","Empty.000","Empty.001","Empty.002","Empty.003","Empty.004","Empty.005",
+                                                        "Empty.006","Empty.007","Empty.008","Empty.009","Empty.010","Empty.011","Empty.012",
+                                                        "Scene.001","Scene.002","Scene.003","Scene.004","Scene.005","Scene.06","Scene.013",
+                                                        "Scene.006","Scene.007","Scene.008","Scene.009","Scene.010","Scene.011","Scene.012",
+                                                        "World","World.000","World.001","World.002","World.003","World.004","World.005" ]
+               self.namesFog=[ "","LINEAR","EXPONENTIAL","" ]
+
+##########################################################
+# Writing nodes routines
+##########################################################
+
+       def writeHeader(self):
+               #bfile = sys.expandpath( Blender.Get('filename') ).replace('<', '&lt').replace('>', '&gt')
+               bfile = self.filename.replace('<', '&lt').replace('>', '&gt') # use outfile name
+               self.file.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+               self.file.write("<!DOCTYPE X3D PUBLIC \"ISO//Web3D//DTD X3D 3.0//EN\" \"http://www.web3d.org/specifications/x3d-3.0.dtd\">\n")
+               self.file.write("<X3D version=\"3.0\" profile=\"Immersive\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema-instance\" xsd:noNamespaceSchemaLocation=\"http://www.web3d.org/specifications/x3d-3.0.xsd\">\n")
+               self.file.write("<head>\n")
+               self.file.write("\t<meta name=\"filename\" content=\"%s\" />\n" % sys.basename(bfile))
+               self.file.write("\t<meta name=\"generator\" content=\"Blender %s\" />\n" % Blender.Get('version'))
+               self.file.write("\t<meta name=\"translator\" content=\"X3D exporter v1.55 (2006/01/17)\" />\n")
+               self.file.write("</head>\n")
+               self.file.write("<Scene>\n")
+       
+       # This functionality is poorly defined, disabling for now - campbell
+       '''
+       def writeInline(self):
+               inlines = Blender.Scene.Get()
+               allinlines = len(inlines)
+               if scene != inlines[0]:
+                       return
+               else:
+                       for i in xrange(allinlines):
+                               nameinline=inlines[i].name
+                               if (nameinline not in self.namesStandard) and (i > 0):
+                                       self.file.write("<Inline DEF=\"%s\" " % (self.cleanStr(nameinline)))
+                                       nameinline = nameinline+".x3d"
+                                       self.file.write("url=\"%s\" />" % nameinline)
+                                       self.file.write("\n\n")
+
+       
+       def writeScript(self):
+               textEditor = Blender.Text.Get() 
+               alltext = len(textEditor)
+               for i in xrange(alltext):
+                       nametext = textEditor[i].name
+                       nlines = textEditor[i].getNLines()
+                       if (self.proto == 1):
+                               if (nametext == "proto" or nametext == "proto.js" or nametext == "proto.txt") and (nlines != None):
+                                       nalllines = len(textEditor[i].asLines())
+                                       alllines = textEditor[i].asLines()
+                                       for j in xrange(nalllines):
+                                               self.writeIndented(alllines[j] + "\n")
+                       elif (self.proto == 0):
+                               if (nametext == "route" or nametext == "route.js" or nametext == "route.txt") and (nlines != None):
+                                       nalllines = len(textEditor[i].asLines())
+                                       alllines = textEditor[i].asLines()
+                                       for j in xrange(nalllines):
+                                          &n