Merge with -r 22620:23107.
authorArystanbek Dyussenov <arystan.d@gmail.com>
Fri, 11 Sep 2009 18:09:58 +0000 (18:09 +0000)
committerArystanbek Dyussenov <arystan.d@gmail.com>
Fri, 11 Sep 2009 18:09:58 +0000 (18:09 +0000)
Next: update scripts and merge in 2.5.

61 files changed:
SConstruct
config/linuxcross-config.py
intern/audaspace/OpenAL/Makefile [deleted file]
intern/audaspace/intern/AUD_FileFactory.cpp [deleted file]
intern/audaspace/intern/AUD_FileFactory.h [deleted file]
release/datafiles/datatoc.py [deleted file]
release/io/export_3ds.py [new file with mode: 0644]
release/io/export_fbx.py [new file with mode: 0644]
release/io/export_obj.py [new file with mode: 0644]
release/io/export_ply.py
release/io/export_x3d.py [new file with mode: 0644]
release/io/import_3ds.py [new file with mode: 0644]
release/io/import_obj.py [new file with mode: 0644]
release/scripts/3ds_export.py
release/scripts/export_fbx.py
release/scripts/import_edl.py [deleted file]
release/ui/buttons_data_empty.py
release/ui/buttons_particle.py [deleted file]
release/ui/space_info.py
release/ui/space_script.py [new file with mode: 0644]
source/blender/blenkernel/BKE_anim.h
source/blender/blenkernel/BKE_image.h
source/blender/blenkernel/SConscript
source/blender/blenkernel/intern/image.c
source/blender/blenkernel/intern/mesh.c
source/blender/editors/include/ED_mesh.h
source/blender/editors/mesh/editmesh_mods.c
source/blender/makesdna/DNA_object_types.h
source/blender/makesdna/intern/SConscript
source/blender/makesrna/RNA_types.h
source/blender/makesrna/intern/makesrna.c
source/blender/makesrna/intern/rna_action.c
source/blender/makesrna/intern/rna_action_api.c [new file with mode: 0644]
source/blender/makesrna/intern/rna_animation_api.c
source/blender/makesrna/intern/rna_armature.c
source/blender/makesrna/intern/rna_image.c
source/blender/makesrna/intern/rna_image_api.c [new file with mode: 0644]
source/blender/makesrna/intern/rna_internal.h
source/blender/makesrna/intern/rna_main_api.c
source/blender/makesrna/intern/rna_material.c
source/blender/makesrna/intern/rna_material_api.c [new file with mode: 0644]
source/blender/makesrna/intern/rna_mesh.c
source/blender/makesrna/intern/rna_mesh_api.c
source/blender/makesrna/intern/rna_object.c
source/blender/makesrna/intern/rna_object_api.c
source/blender/makesrna/intern/rna_pose.c
source/blender/makesrna/intern/rna_pose_api.c [new file with mode: 0644]
source/blender/makesrna/intern/rna_scene_api.c
source/blender/makesrna/intern/rna_test.c
source/blender/makesrna/intern/rna_ui_api.c
source/blender/python/intern/bpy_interface.c
source/blender/python/intern/bpy_rna.c
source/blender/python/intern/bpy_sys.c [new file with mode: 0644]
source/blender/python/intern/bpy_sys.h [new file with mode: 0644]
source/blender/python/intern/bpy_util.c
source/creator/SConscript
source/creator/creator.c
source/creator/tests/test.c [new file with mode: 0644]
source/creator/tests/test.h [new file with mode: 0644]
tools/Blender.py
tools/btools.py

index 83ec6c1..bacb158 100644 (file)
@@ -401,7 +401,8 @@ thestatlibs, thelibincs = B.setup_staticlibs(env)
 thesyslibs = B.setup_syslibs(env)
 
 if 'blender' in B.targets or not env['WITH_BF_NOBLENDER']:
-       env.BlenderProg(B.root_build_dir, "blender", dobj + mainlist, [], thestatlibs + thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender')
+       blen = env.BlenderProg(B.root_build_dir, "blender", dobj + mainlist, [], thestatlibs + thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender')
+
 if env['WITH_BF_PLAYER']:
        playerlist = B.create_blender_liblist(env, 'player')
        playerlist += B.create_blender_liblist(env, 'intern')
index a7ce2dc..9f79fab 100644 (file)
@@ -2,13 +2,13 @@ LCGDIR = '#../lib/windows'
 LIBDIR = '${LCGDIR}'
 
 BF_PYTHON = LIBDIR + '/python'
-BF_PYTHON_VERSION = '2.5'
+BF_PYTHON_VERSION = '2.6'
 BF_PYTHON_INC = '${BF_PYTHON}/include/python${BF_PYTHON_VERSION}'
 BF_PYTHON_BINARY = 'python'
-BF_PYTHON_LIB = 'python25'
+BF_PYTHON_LIB = 'python26'
 BF_PYTHON_LIBPATH = '${BF_PYTHON}/lib'
 
-WITH_BF_OPENAL = True
+WITH_BF_OPENAL = False # XXX (Arystan)
 WITH_BF_STATICOPENAL = False
 BF_OPENAL = LIBDIR + '/openal'
 BF_OPENAL_INC = '${BF_OPENAL}/include'
@@ -17,6 +17,12 @@ BF_OPENAL_LIBPATH = '${BF_OPENAL}/lib'
 # Warning, this static lib configuration is untested! users of this OS please confirm.
 BF_OPENAL_LIB_STATIC = '${BF_OPENAL}/lib/libopenal.a'
 
+# copied from win32-mingw-config.py (Arystan)
+WITH_BF_FFMPEG = False
+BF_FFMPEG_LIB = 'avformat swscale avcodec avutil avdevice xvidcore x264'
+BF_FFMPEG_LIBPATH = LIBDIR + '/gcc/ffmpeg/lib'
+BF_FFMPEG_INC =  LIBDIR + '/gcc/ffmpeg/include'
+
 # Warning, this static lib configuration is untested! users of this OS please confirm.
 BF_CXX = '/usr'
 WITH_BF_STATICCXX = False
@@ -38,7 +44,7 @@ BF_PTHREADS_INC = '${BF_PTHREADS}/include'
 BF_PTHREADS_LIB = 'pthreadGC2'
 BF_PTHREADS_LIBPATH = '${BF_PTHREADS}/lib'
 
-WITH_BF_OPENEXR = True
+WITH_BF_OPENEXR = False
 WITH_BF_STATICOPENEXR = False
 BF_OPENEXR = LIBDIR + '/gcc/openexr'
 BF_OPENEXR_INC = '${BF_OPENEXR}/include ${BF_OPENEXR}/include/OpenEXR'
@@ -63,6 +69,8 @@ BF_PNG_LIBPATH = '${BF_PNG}/lib'
 
 BF_TIFF = LIBDIR + '/tiff'
 BF_TIFF_INC = '${BF_TIFF}/include'
+BF_TIFF_LIB = 'libtiff'
+BF_TIFF_LIBPATH = '${BF_TIFF}/lib'
 
 WITH_BF_ZLIB = True
 BF_ZLIB = LIBDIR + '/zlib'
@@ -75,16 +83,26 @@ WITH_BF_INTERNATIONAL = True
 BF_GETTEXT = LIBDIR + '/gettext'
 BF_GETTEXT_INC = '${BF_GETTEXT}/include'
 BF_GETTEXT_LIB = 'gnu_gettext'
+# BF_GETTEXT_LIB = 'gnu_gettext'
 BF_GETTEXT_LIBPATH = '${BF_GETTEXT}/lib'
 
 WITH_BF_GAMEENGINE = False
 WITH_BF_PLAYER = False
 
+WITH_BF_ODE = True
+BF_ODE = LIBDIR + '/ode'
+BF_ODE_INC = BF_ODE + '/include'
+BF_ODE_LIB = BF_ODE + '/lib/libode.a'
+
 WITH_BF_BULLET = True
 BF_BULLET = '#extern/bullet2/src'
 BF_BULLET_INC = '${BF_BULLET}'
 BF_BULLET_LIB = 'extern_bullet'
 
+BF_SOLID = '#extern/solid'
+BF_SOLID_INC = '${BF_SOLID}'
+BF_SOLID_LIB = 'extern_solid'
+
 BF_WINTAB = LIBDIR + '/wintab'
 BF_WINTAB_INC = '${BF_WINTAB}/INCLUDE'
 
@@ -106,9 +124,12 @@ BF_ICONV_LIBPATH = '${BF_ICONV}/lib'
 
 # Mesa Libs should go here if your using them as well....
 WITH_BF_STATICOPENGL = False
-BF_OPENGL = 'C:\\MingW'
-BF_OPENGL_INC = '${BF_OPENGL}/include'
-BF_OPENGL_LIBINC = '${BF_OPENGL}/lib'
+BF_OPENGL = ''
+# BF_OPENGL = 'C:\\MingW'
+BF_OPENGL_INC = ''
+# BF_OPENGL_INC = '${BF_OPENGL}/include'
+BF_OPENGL_LIBINC = ''
+# BF_OPENGL_LIBINC = '${BF_OPENGL}/lib'
 BF_OPENGL_LIB = 'opengl32 glu32'
 BF_OPENGL_LIB_STATIC = [ '${BF_OPENGL}/lib/libGL.a', '${BF_OPENGL}/lib/libGLU.a',
              '${BF_OPENGL}/lib/libXmu.a', '${BF_OPENGL}/lib/libXext.a',
@@ -117,9 +138,13 @@ BF_OPENGL_LIB_STATIC = [ '${BF_OPENGL}/lib/libGL.a', '${BF_OPENGL}/lib/libGLU.a'
 CC = 'i586-mingw32msvc-gcc'
 CXX = 'i586-mingw32msvc-g++'
 
+# Custom built MinGW (Arystan)
+# CC = 'i586-pc-mingw32-gcc'
+# CXX = 'i586-pc-mingw32-g++'
+
 CCFLAGS = [ '-pipe', '-funsigned-char', '-fno-strict-aliasing' ]
 
-CPPFLAGS = [ '-DXP_UNIX', '-DWIN32', '-DFREE_WINDOWS' ]
+CPPFLAGS = [ '-DXP_UNIX', '-DWIN32', '-DFREE_WINDOWS', '-DLINUX_CROSS' ]
 CXXFLAGS = ['-pipe', '-mwindows', '-funsigned-char', '-fno-strict-aliasing' ]
 REL_CFLAGS = [ '-O2' ]
 REL_CCFLAGS = [ '-O2' ]
@@ -128,7 +153,7 @@ C_WARN = [ '-Wall' , '-Wno-char-subscripts', '-Wdeclaration-after-statement' ]
 CC_WARN = [ '-Wall' ]
 
 
-LLIBS = [ '-ldxguid', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz'] #'-lutil', '-lc', '-lm', '-ldl', '-lpthread' ]
+LLIBS = [ '-ldxguid', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++'] #'-lutil', '-lc', '-lm', '-ldl', '-lpthread' ]
 
 BF_DEBUG = False
 BF_DEBUG_CCFLAGS= []
@@ -140,3 +165,5 @@ BF_PROFILE_LINKFLAGS = ['-pg']
 BF_BUILDDIR = '../build/linuxcross'
 BF_INSTALLDIR='../install/linuxcross'
 BF_DOCDIR='../install/doc'
+
+# LINKFLAGS = ['-Wl,--start-group']
diff --git a/intern/audaspace/OpenAL/Makefile b/intern/audaspace/OpenAL/Makefile
deleted file mode 100644 (file)
index 4cf9f66..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# $Id$
-#
-# ***** 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.
-#
-# The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
-# All rights reserved.
-#
-# The Original Code is: all of this file.
-#
-# Contributor(s): none yet.
-#
-# ***** END GPL LICENSE BLOCK *****
-#
-#
-
-LIBNAME = aud_openal
-DIR = $(OCGDIR)/intern/audaspace
-
-include nan_compile.mk
-
-CCFLAGS += $(LEVEL_1_CPP_WARNINGS)
-
-CPPFLAGS += -I../intern
-CPPFLAGS += -I.
diff --git a/intern/audaspace/intern/AUD_FileFactory.cpp b/intern/audaspace/intern/AUD_FileFactory.cpp
deleted file mode 100644 (file)
index b633908..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * $Id$
- *
- * ***** BEGIN LGPL LICENSE BLOCK *****
- *
- * Copyright 2009 Jörg Hermann Müller
- *
- * This file is part of AudaSpace.
- *
- * AudaSpace is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * AudaSpace 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with AudaSpace.  If not, see <http://www.gnu.org/licenses/>.
- *
- * ***** END LGPL LICENSE BLOCK *****
- */
-
-#include "AUD_FileFactory.h"
-#include "AUD_Buffer.h"
-
-#include <cstring>
-
-#ifdef WITH_FFMPEG
-#include "AUD_FFMPEGReader.h"
-#endif
-#ifdef WITH_SNDFILE
-#include "AUD_SndFileReader.h"
-#endif
-
-AUD_FileFactory::AUD_FileFactory(const char* filename)
-{
-       if(filename != NULL)
-       {
-               m_filename = new char[strlen(filename)+1]; AUD_NEW("string")
-               strcpy(m_filename, filename);
-       }
-       else
-               m_filename = NULL;
-}
-
-AUD_FileFactory::AUD_FileFactory(unsigned char* buffer, int size)
-{
-       m_filename = NULL;
-       m_buffer = AUD_Reference<AUD_Buffer>(new AUD_Buffer(size));
-       memcpy(m_buffer.get()->getBuffer(), buffer, size);
-}
-
-AUD_FileFactory::~AUD_FileFactory()
-{
-       if(m_filename)
-       {
-               delete[] m_filename; AUD_DELETE("string")
-       }
-}
-
-AUD_IReader* AUD_FileFactory::createReader()
-{
-       AUD_IReader* reader = 0;
-
-#ifdef WITH_SNDFILE
-       try
-       {
-               if(m_filename)
-                       reader = new AUD_SndFileReader(m_filename);
-               else
-                       reader = new AUD_SndFileReader(m_buffer);
-               AUD_NEW("reader")
-               return reader;
-       }
-       catch(AUD_Exception e) {}
-#endif
-
-#ifdef WITH_FFMPEG
-       try
-       {
-               if(m_filename)
-                       reader = new AUD_FFMPEGReader(m_filename);
-               else
-                       reader = new AUD_FFMPEGReader(m_buffer);
-               AUD_NEW("reader")
-               return reader;
-       }
-       catch(AUD_Exception e) {}
-#endif
-
-       return reader;
-}
diff --git a/intern/audaspace/intern/AUD_FileFactory.h b/intern/audaspace/intern/AUD_FileFactory.h
deleted file mode 100644 (file)
index 6ab8f28..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * $Id$
- *
- * ***** BEGIN LGPL LICENSE BLOCK *****
- *
- * Copyright 2009 Jörg Hermann Müller
- *
- * This file is part of AudaSpace.
- *
- * AudaSpace is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * AudaSpace 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with AudaSpace.  If not, see <http://www.gnu.org/licenses/>.
- *
- * ***** END LGPL LICENSE BLOCK *****
- */
-
-#ifndef AUD_FILEFACTORY
-#define AUD_FILEFACTORY
-
-#include "AUD_IFactory.h"
-#include "AUD_Reference.h"
-class AUD_Buffer;
-
-/**
- * This factory tries to read a sound file via all available file readers.
- */
-class AUD_FileFactory : public AUD_IFactory
-{
-private:
-       /**
-        * The filename of the sound source file.
-        */
-       char* m_filename;
-
-       /**
-        * The buffer to read from.
-        */
-       AUD_Reference<AUD_Buffer> m_buffer;
-
-public:
-       /**
-        * Creates a new factory.
-        * \param filename The sound file path.
-        */
-       AUD_FileFactory(const char* filename);
-
-       /**
-        * Creates a new factory.
-        * \param buffer The buffer to read from.
-        * \param size The size of the buffer.
-        */
-       AUD_FileFactory(unsigned char* buffer, int size);
-
-       /**
-        * Destroys the factory.
-        */
-       ~AUD_FileFactory();
-
-       virtual AUD_IReader* createReader();
-};
-
-#endif //AUD_FILEFACTORY
diff --git a/release/datafiles/datatoc.py b/release/datafiles/datatoc.py
deleted file mode 100755 (executable)
index 362d4dd..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# ***** 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.
-#
-# The Original Code is Copyright (C) 2009 Blender Foundation.
-# All rights reserved.
-#
-# Contributor(s): Jörg Müller
-#
-# ***** END GPL LICENCE BLOCK *****
-
-import sys, os
-
-if len(sys.argv) < 2:
-       sys.stdout.write("Usage: datatoc <data_file>\n")
-       sys.exit(1)
-
-filename = sys.argv[1]
-
-try:
-       fpin = open(filename, "rb");
-except:
-       sys.stdout.write("Unable to open input %s\n" % sys.argv[1])
-       sys.exit(1)
-
-fpin.seek(0, os.SEEK_END)
-size = fpin.tell()
-fpin.seek(0)
-
-if filename[0] == ".":
-       filename = filename[1:]
-
-cname = filename + ".c"
-sys.stdout.write("Making C file <%s>\n" % cname)
-
-filename = filename.replace(".", "_")
-sys.stdout.write(str(size))
-sys.stdout.write("\n")
-try:
-       fpout = open(cname, "w")
-except:
-       sys.stdout.write("Unable to open output %s\n" % cname)
-       sys.exit(1)
-
-fpout.write("/* DataToC output of file <%s> */\n\n" % filename)
-fpout.write("int datatoc_%s_size= %d;\n" % (filename, size))
-
-fpout.write("char datatoc_%s[]= {\n" % filename)
-
-while size > 0:
-       size -= 1
-       if size % 32 == 31:
-               fpout.write("\n")
-       
-       fpout.write("%3d," % ord(fpin.read(1)))
-
-fpout.write("\n  0};\n\n")
-
-fpin.close()
-fpout.close()
diff --git a/release/io/export_3ds.py b/release/io/export_3ds.py
new file mode 100644 (file)
index 0000000..19c1214
--- /dev/null
@@ -0,0 +1,1128 @@
+#!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 struct
+import os
+import time
+
+import bpy
+
+# import Blender
+# from BPyMesh import getMeshFromObject
+# from BPyObject import getDerivedObjects
+# try: 
+#     import struct
+# except: 
+#     struct = None
+
+# also used by X3D exporter
+# return a tuple (free, object list), free is True if memory should be freed later with free_derived_objects()
+def create_derived_objects(ob):
+       if ob.parent and ob.parent.dupli_type != 'NONE':
+               return False, None
+
+       if ob.dupli_type != 'NONE':
+               ob.create_dupli_list()
+               return True, [(dob.object, dob.matrix) for dob in ob.dupli_list]
+       else:
+               return False, [(ob, ob.matrix)]
+
+# also used by X3D exporter
+def free_derived_objects(ob):
+       ob.free_dupli_list()
+
+# 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= int("0x4D4D",16)
+
+#------ Main Chunks
+OBJECTINFO   =      int("0x3D3D",16);      #This gives the version of the mesh and is found right before the material and object information
+VERSION      =      int("0x0002",16);      #This gives the version of the .3ds file
+KFDATA       =      int("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    =      int("0xA000",16);      # This holds the material name
+MATAMBIENT   =      int("0xA010",16);      # Ambient color of the object/material
+MATDIFFUSE   =      int("0xA020",16);      # This holds the color of the object/material
+MATSPECULAR   =      int("0xA030",16);      # SPecular color of the object/material
+MATSHINESS   =      int("0xA040",16);      # ??
+MATMAP       =      int("0xA200",16);      # This is a header for a new material
+MATMAPFILE    =      int("0xA300",16);      # This holds the file name of the texture
+
+RGB1=  int("0x0011",16)
+RGB2=  int("0x0012",16)
+
+#>------ sub defines of OBJECT
+OBJECT_MESH  =      int("0x4100",16);      # This lets us know that we are reading a new object
+OBJECT_LIGHT =      int("0x4600",16);      # This lets un know we are reading a light object
+OBJECT_CAMERA=      int("0x4700",16);      # This lets un know we are reading a camera object
+
+#>------ sub defines of CAMERA
+OBJECT_CAM_RANGES=   int("0x4720",16);      # The camera range values
+
+#>------ sub defines of OBJECT_MESH
+OBJECT_VERTICES =   int("0x4110",16);      # The objects vertices
+OBJECT_FACES    =   int("0x4120",16);      # The objects faces
+OBJECT_MATERIAL =   int("0x4130",16);      # This is found if the object has a material, either texture map or color
+OBJECT_UV       =   int("0x4140",16);      # The UV texture coordinates
+OBJECT_TRANS_MATRIX  =   int("0x4160",16); # The Object Matrix
+
+#>------ sub defines of KFDATA
+KFDATA_KFHDR            = int("0xB00A",16);
+KFDATA_KFSEG            = int("0xB008",16);
+KFDATA_KFCURTIME        = int("0xB009",16);
+KFDATA_OBJECT_NODE_TAG  = int("0xB002",16);
+
+#>------ sub defines of OBJECT_NODE_TAG
+OBJECT_NODE_ID          = int("0xB030",16);
+OBJECT_NODE_HDR         = int("0xB010",16);
+OBJECT_PIVOT            = int("0xB013",16);
+OBJECT_INSTANCE_NAME    = int("0xB011",16);
+POS_TRACK_TAG                  = int("0xB020",16);
+ROT_TRACK_TAG                  = int("0xB021",16);
+SCL_TRACK_TAG                  = int("0xB022",16);
+
+def uv_key(uv):
+       return round(uv[0], 6), round(uv[1], 6)
+#      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('<3B', int(255*self.r), int(255*self.g), int(255*self.b) ) )
+#              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 range(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 range(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.
+       if material:
+               return [s.texture.image for s in material.textures if s and s.texture.type == 'IMAGE' and s.texture.image]
+
+       return []
+#      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 = os.path.basename(image.filename)
+#              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.ambient for a in material.diffuse_color] ))
+#              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.diffuse_color))
+#              material_chunk.add_subchunk(make_material_subchunk(MATDIFFUSE, material.rgbCol))
+               material_chunk.add_subchunk(make_material_subchunk(MATSPECULAR, material.specular_color))
+#              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 = len(mesh.uv_textures)
+#      do_uv = mesh.faceUV
+       
+#      if not do_uv:
+#              face_uv = None
+       
+       img = None
+       for i, face in enumerate(mesh.faces):
+               f_v = face.verts
+#              f_v = face.v
+
+               uf = mesh.active_uv_texture.data[i] if do_uv else None
+               
+               if do_uv:
+                       f_uv =  (uf.uv1, uf.uv2, uf.uv3, uf.uv4) if face.verts[3] else (uf.uv1, uf.uv2, uf.uv3)
+#                      f_uv = face.uv
+                       img = uf.image if uf else None
+#                      img = face.image
+                       if img: img = img.name
+
+               if f_v[3] == 0:
+               # if len(f_v)==3:
+                       new_tri = tri_wrapper((f_v[0], f_v[1], f_v[2]), face.material_index, img)
+#                      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], f_v[1], f_v[2]), face.material_index, img)
+#                      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], f_v[2], f_v[3]), face.material_index, 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 range(len(verts))]
+       
+       # for each face uv coordinate, add it to the UniqueList of the vertex
+       for tri in tri_list:
+               for i in range(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].values():
+                       # 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 range(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 len(mesh.uv_textures):
+#      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.values():
+                       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 range(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 len(mesh.uv_textures):
+#      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 len(mesh.sticky):
+#              if mesh.vertexUV:
+                       uv_array = _3ds_array()
+                       for uv in mesh.sticky:
+#                      for vert in mesh.verts:
+                               uv_array.add(_3ds_point_uv(uv.co))
+#                              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, context):
+       '''Save the Blender scene to a 3ds file.'''
+       # Time the export
+       
+       if not filename.lower().endswith('.3ds'):
+               filename += '.3ds'
+
+       # XXX
+#      if not BPyMessages.Warning_SaveOver(filename):
+#              return
+
+       # XXX
+       time1 = time.clock()
+#      time1= Blender.sys.time()
+#      Blender.Window.WaitCursor(1)
+
+       sce = context.scene
+#      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 [ob for ob in context.scene.objects if ob.is_visible()]:
+#      for ob in sce.objects.context:
+
+               # get derived objects
+               free, derived = create_derived_objects(ob)
+
+               if derived == None: continue
+
+               for ob_derived, mat in derived:
+#              for ob_derived, mat in getDerivedObjects(ob, False):
+
+                       if ob.type not in ('MESH', 'CURVE', 'SURFACE', 'TEXT', 'META'):
+                               continue
+
+                       data = ob_derived.create_mesh(True, 'PREVIEW')
+#                      data = getMeshFromObject(ob_derived, None, True, False, sce)
+                       if data:
+                               data.transform(mat)
+#                              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 len(data.uv_textures):
+#                              if data.faceUV:
+                                       if not mat_ls:
+                                               mat = mat_name = None
+                                       
+                                       for f, uf in zip(data.faces, data.active_uv_texture.data):
+                                               if mat_ls:
+                                                       mat_index = f.material_index
+#                                                      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 = uf.image
+#                                              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.material_index >= mat_ls_len:
+#                                              if f.mat >= mat_ls_len:
+                                                       f.material_index = 0
+                                                       # f.mat = 0
+
+               if free:
+                       free_derived_objects(ob)
+
+
+       # Make material chunks for all materials used in the meshes:
+       for mat_and_image in materialDict.values():
+               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))
+               '''
+#              if not blender_mesh.users:
+               bpy.data.remove_mesh(blender_mesh)
+#              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" % (time.clock() - time1))
+#      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')
+
+class EXPORT_OT_3ds(bpy.types.Operator):
+       '''
+       3DS Exporter
+       '''
+       __idname__ = "export.3ds"
+       __label__ = 'Export 3DS'
+       
+       # List of operator properties, the attributes will be assigned
+       # to the class instance from the operator settings before calling.
+
+       __props__ = [
+               bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for exporting the 3DS file", maxlen= 1024, default= ""),
+       ]
+       
+       def execute(self, context):
+               save_3ds(self.filename, context)
+               return ('FINISHED',)
+       
+       def invoke(self, context, event):
+               wm = context.manager
+               wm.add_fileselect(self.__operator__)
+               return ('RUNNING_MODAL',)
+       
+       def poll(self, context): # Poll isnt working yet
+               print("Poll")
+               return context.active_object != None
+
+bpy.ops.add(EXPORT_OT_3ds)
diff --git a/release/io/export_fbx.py b/release/io/export_fbx.py
new file mode 100644 (file)
index 0000000..3515170
--- /dev/null
@@ -0,0 +1,3457 @@
+#!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 *****
+# --------------------------------------------------------------------------
+
+import os
+import time
+import math # math.pi
+import shutil # for file copying
+
+# 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
+import Mathutils
+# 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(os.sep):
+               dest_dir += os.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)
+
+# I guess FBX uses degrees instead of radians (Arystan).
+# Call this function just before writing to FBX.
+def eulerRadToDeg(eul):
+       ret = Mathutils.Euler()
+
+       ret.x = 180 / math.pi * eul[0]
+       ret.y = 180 / math.pi * eul[1]
+       ret.z = 180 / math.pi * eul[2]
+
+       return ret
+
+mtx4_identity = Mathutils.Matrix()
+
+# testing
+mtx_x90                = Mathutils.RotationMatrix( math.pi/2, 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      = Mathutils.RotationMatrix(-math.pi/2, 4, 'x') # used
+#mtx4_y90      = RotationMatrix( 90, 4, 'y')
+mtx4_y90n      = Mathutils.RotationMatrix(-math.pi/2, 4, 'y') # used
+mtx4_z90       = Mathutils.RotationMatrix( math.pi/2, 4, 'z') # used
+mtx4_z90n      = Mathutils.RotationMatrix(-math.pi/2, 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 iter(dct.values()):       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 = bpy.sys.expandpath(fname_orig)
+# #    fname = Blender.sys.expandpath(fname_orig)
+#      fname_strip = os.path.basename(fname)
+# #    fname_strip = strip_path(fname)
+#      if FORCE_CWD:
+#              fname_rel = '.' + os.sep + fname_strip
+#      else:
+#              fname_rel = bpy.sys.relpath(fname, basepath)
+# #            fname_rel = Blender.sys.relpath(fname, basepath)
+#      if fname_rel.startswith('//'): fname_rel = '.' + os.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 ])
+
+# XXX not used
+# duplicated in OBJ exporter
+def getVertsFromGroup(me, group_index):
+       ret = []
+
+       for i, v in enumerate(me.verts):
+               for g in v.groups:
+                       if g.group == group_index:
+                               ret.append((i, g.weight))
+
+               return ret
+
+# ob must be OB_MESH
+def BPyMesh_meshWeight2List(ob):
+       ''' Takes a mesh and return its group names and a list of lists, one list per vertex.
+       aligning the each vert list with the group names, each list contains float value for the weight.
+       These 2 lists can be modified and then used with list2MeshWeight to apply the changes.
+       '''
+
+       me = ob.data
+
+       # Clear the vert group.
+       groupNames= [g.name for g in ob.vertex_groups]
+       len_groupNames= len(groupNames)
+       
+       if not len_groupNames:
+               # no verts? return a vert aligned empty list
+               return [[] for i in range(len(me.verts))], []
+       else:
+               vWeightList= [[0.0]*len_groupNames for i in range(len(me.verts))]
+
+       for i, v in enumerate(me.verts):
+               for g in v.groups:
+                       vWeightList[i][g.group] = g.weight
+
+       return groupNames, vWeightList
+
+def meshNormalizedWeights(me):
+       try: # account for old bad BPyMesh
+               groupNames, vWeightList = BPyMesh_meshWeight2List(me)
+#              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, \
+               context = 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 =                         Mathutils.Matrix(),
+               ANIM_ENABLE =                           True,
+               ANIM_OPTIMIZE =                         True,
+               ANIM_OPTIMIZE_PRECISSION =      6,
+               ANIM_ACTION_ALL =                       False,
+               BATCH_ENABLE =                          False,
+               BATCH_GROUP =                           True,
+               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 = bpy.sys.exists(fbxpath)
+#              tmp_exists = Blender.sys.exists(fbxpath)
+               
+               if tmp_exists != 2: # a file, we want a path
+                       fbxpath = os.path.dirname(fbxpath)
+#                      while fbxpath and fbxpath[-1] not in ('/', '\\'):
+#                              fbxpath = fbxpath[:-1]
+                       if not fbxpath:
+#                      if not filename:
+                               # XXX
+                               print('Error%t|Directory does not exist!')
+#                              Draw.PupMenu('Error%t|Directory does not exist!')
+                               return
+
+                       tmp_exists = bpy.sys.exists(fbxpath)
+#                      tmp_exists = Blender.sys.exists(fbxpath)
+               
+               if tmp_exists != 2:
+                       # XXX
+                       print('Error%t|Directory does not exist!')
+#                      Draw.PupMenu('Error%t|Directory does not exist!')
+                       return
+               
+               if not fbxpath.endswith(os.sep):
+                       fbxpath += os.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 = context.scene
+#              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 + cleanName(data.name)
+#                      newname = BATCH_FILE_PREFIX + BPySys.cleanName(data.name)
+                       
+                       
+                       if BATCH_OWN_DIR:
+                               new_fbxpath = fbxpath + newname + os.sep
+                               # path may alredy exist
+                               # TODO - might exist but be a file. unlikely but should probably account for it.
+
+                               if bpy.sys.exists(new_fbxpath) == 0:
+#                              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))
+
+                       # XXX don't know what to do with this, probably do the same? (Arystan)
+                       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,
+                               context,
+                               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.remove_scene(sce)
+#                              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 = os.path.dirname(filename) or '.'
+       basepath += os.sep
+#      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.armature_matrix
+#                      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.pose
+#                      pose = fbxArm.blenObject.getPose()
+                       self.__pose_bone =              pose.pose_channels[self.blenName]
+#                      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.pose_matrix.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.matrix * 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.matrix.copy()
+#                      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 type =='CAMERA':
+#                      elif ob and type =='Camera':
+                               y = Mathutils.Vector(0,1,0) * matrix_rot
+                               matrix_rot = matrix_rot * Mathutils.RotationMatrix(math.pi/2, 3, 'r', y)
+                       
+                       return matrix_rot
+                       
+       # ----------------------------------------------
+       
+       
+       
+       
+       
+       print('\nFBX export starting...', filename)
+       start_time = time.clock()
+#      start_time = Blender.sys.time()
+       try:
+               file = open(filename, 'w')
+       except:
+               return False
+
+       sce = context.scene
+#      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 2.5"')
+#      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, bpy.types.Bone):
+#              if isinstance(ob, Blender.Types.BoneType):
+                       
+                       # we know we have a matrix
+                       # matrix = mtx4_z90 * (ob.matrix['ARMATURESPACE'] * matrix_mod)
+                       matrix = mtx4_z90 * ob.armature_matrix # dont apply armature matrix anymore
+#                      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.armature_matrix # dont apply armature matrix anymore
+#                              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 Exception("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 = Mathutils.Vector(0,1,0) * matrix_rot
+                                       matrix_rot = matrix_rot * Mathutils.RotationMatrix(math.pi/2, 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' % tuple(eulerRadToDeg(rot)))
+#              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 not isinstance(ob, bpy.types.Bone):
+#              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.armature_head - my_bone.blenBone.armature_tail).length)
+#                      (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_data
+               width   = render.resolution_x
+               height  = render.resolution_y
+#              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.shift_x) # not sure if this is in the correct units?
+#              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.shift_y) # ditto 
+#              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.clip_start)
+#              file.write('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % data.clipStart)
+               file.write('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % data.clip_end)
+#              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(Mathutils.Vector(0,1,0) * matrix_rot) )
+               file.write('\n\t\tLookAt: %.6f,%.6f,%.6f' % tuple(Mathutils.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_items = {'POINT': 0, 'SUN': 1, 'SPOT': 2, 'HEMI': 3, 'AREA': 4}
+               light_type = light_type_items[light.type]
+#              light_type = light.type
+               if light_type > 2: light_type = 1 # hemi and area lights become directional
+
+#              mode = light.mode
+               if light.shadow_method == 'RAY_SHADOW' or light.shadow_method == 'BUFFER_SHADOW':
+#              if mode & Blender.Lamp.Modes.RayShadow or mode & Blender.Lamp.Modes.Shadows:
+                       do_shadow = 1
+               else:
+                       do_shadow = 0
+
+               if light.only_shadow or (not light.diffuse and not light.specular):
+#              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
+               if light.type == 'SPOT':
+                       file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spot_size * scale))
+#              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.color))
+#              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
+# 
+               # duplication? see ^ (Arystan)
+#              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.distance)
+#              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 = tuple(world.ambient_color)
+#      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.diffuse_color)
+#                      mat_cold = tuple(mat.rgbCol)
+                       mat_cols = tuple(mat.specular_color)
+#                      mat_cols = tuple(mat.specCol)
+                       #mat_colm = tuple(mat.mirCol) # we wont use the mirror color
+                       mat_colamb = world_amb
+#                      mat_colamb = tuple([c for c in world_amb])
+
+                       mat_dif = mat.diffuse_reflection
+#                      mat_dif = mat.ref
+                       mat_amb = mat.ambient
+#                      mat_amb = mat.amb
+                       mat_hard = (float(mat.specular_hardness)-1)/5.10
+#                      mat_hard = (float(mat.hard)-1)/5.10
+                       mat_spec = mat.specular_reflection/2.0
+#                      mat_spec = mat.spec/2.0
+                       mat_alpha = mat.alpha
+                       mat_emit = mat.emit
+                       mat_shadeless = mat.shadeless
+#                      mat_shadeless = mat.mode & Blender.Material.Modes.SHADELESS
+                       if mat_shadeless:
+                               mat_shader = 'Lambert'
+                       else:
+                               if mat.diffuse_shader == 'LAMBERT':
+#                              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 copy_image(image):
+
+               rel = image.get_export_path(basepath, True)
+               base = os.path.basename(rel)
+
+               if EXP_IMAGE_COPY:
+                       src = bpy.sys.expandpath(image.filename)
+                       absp = image.get_export_path(basepath, False)
+                       if not os.path.exists(absp):
+                               shutil.copy(src, absp)
+
+               return (rel, base)
+
+       # tex is an Image (Arystan)
+       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_rel, fname_strip = copy_image(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.clamp_x)
+#              file.write('\n\t\t\tProperty: "WrapModeU", "enum", "",%i' % tex.clampX)
+               file.write('\n\t\t\tProperty: "WrapModeV", "enum", "",%i' % tex.clamp_y)
+#              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_rel, fname_strip = copy_image(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 range(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 = len(me.uv_textures) > 0
+#              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 j, v_index in enumerate(f.verts) if v_index != 0 or j != 3]
+#                      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(fi) == 3:        file.write('%i,%i,%i' % fi )
+#                              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(fi) == 3:        file.write(',%i,%i,%i' % fi )
+#                              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.verts[0], ed.verts[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.verts[0], ed.verts[1]))
+#                                      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.normal));      i=0
+#                              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.normal))
+#                              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: 0 {
+                       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.sharp));  i=0
+#                              file.write('%i' % ((ed.flag&SHARP)!=0));        i=0
+                       else:
+                               if i==54:
+                                       file.write('\n                   ');    i=0
+                               file.write(',%i' % (ed.sharp))
+#                              file.write(',%i' % ((ed.flag&SHARP)!=0))
+                       i+=1
+               
+               file.write('\n\t\t}')
+#              del SHARP
+
+               # small utility function
+               # returns a slice of data depending on number of face verts
+               # data is either a MeshTextureFace or MeshColor
+               def face_data(data, face):
+                       if f.verts[3] == 0:
+                               totvert = 3
+                       else:
+                               totvert = 4
+                                               
+                       return data[:totvert]
+
+               
+               # Write VertexColor Layers
+               # note, no programs seem to use this info :/
+               collayers = []
+               if len(me.vertex_colors):
+#              if me.vertexColors:
+                       collayers = me.vertex_colors
+#                      collayers = me.getColorLayerNames()
+                       collayer_orig = me.active_vertex_color
+#                      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.name)
+#                              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, cf in zip(me.faces, collayer.data):
+                                       colors = [cf.color1, cf.color2, cf.color3, cf.color4]
+
+                                       # determine number of verts
+                                       colors = face_data(colors, f)
+
+                                       for col in colors:
+                                               if i==-1:
+                                                       file.write('%.4f,%.4f,%.4f,1' % tuple(col))
+                                                       i=0
+                                               else:
+                                                       if i==7:
+                                                               file.write('\n\t\t\t\t')
+                                                               i=0
+                                                       file.write(',%.4f,%.4f,%.4f,1' % tuple(col))
+                                               i+=1
+                                               ii+=1 # One more Color
+
+#                              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 range(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.uv_textures
+#                      uvlayers = me.getUVLayerNames()
+                       uvlayer_orig = me.active_uv_texture
+#                      uvlayer_orig = me.activeUVLayer
+                       for uvindex, uvlayer in enumerate(me.uv_textures):
+#                      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.name)
+#                              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, uf in zip(me.faces, uvlayer.data):
+#                              for f in me.faces:
+                                       uvs = [uf.uv1, uf.uv2, uf.uv3, uf.uv4]
+                                       uvs = face_data(uvs, f)
+                                       
+                                       for uv in uvs:
+#                                      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 range(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.name)
+#                                      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 uvlayer.data:
+#                                              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
+
+                               if me.active_uv_texture:
+                                       uv_faces = me.active_uv_texture.data
+                               else:
+                                       uv_faces = [None] * len(me.faces)
+                               
+                               i=-1
+                               for f, uf in zip(me.faces, uv_faces):
+#                              for f in me.faces:
+                                       try:    mat = mats[f.material_index]
+#                                      try:    mat = mats[f.mat]
+                                       except:mat = None
+                                       
+                                       if do_uvs: tex = uf.image # WARNING - MULTI UV LAYER IMAGES NOT SUPPORTED :/
+#                                      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.vertex_colors:
+#              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 range(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 range(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 = context.selected_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.rest_position for arm in bpy.data.armatures]
+#              ob_arms_orig_rest = [arm.restPosition for arm in bpy.data.armatures]
+               
+               for arm in bpy.data.armatures:
+                       arm.rest_position = True
+#                      arm.restPosition = True
+               
+               if ob_arms_orig_rest:
+                       for ob_base in bpy.data.objects:
+                               #if ob_base.type == 'Armature':
+                               ob_base.make_display_list()
+#                              ob_base.makeDisplayList()
+                                       
+                       # This causes the makeDisplayList command to effect the mesh
+                       sce.set_frame(sce.current_frame)
+#                      Blender.Set('curframe', Blender.Get('curframe'))
+                       
+       
+       for ob_base in tmp_objects:
+
+               # ignore dupli children
+               if ob_base.parent and ob_base.parent.dupli_type != 'NONE':
+                       continue
+
+               obs = [(ob_base, ob_base.matrix)]
+               if ob_base.dupli_type != 'NONE':
+                       ob_base.create_dupli_list()
+                       obs = [(dob.object, dob.matrix) for dob in ob_base.dupli_list]
+
+               for ob, mtx in obs:
+#              for ob, mtx in BPyObject.getDerivedObjects(ob_base):
+                       tmp_ob_type = ob.type
+                       if tmp_ob_type == 'CAMERA':
+#                      if tmp_ob_type == 'Camera':
+                               if EXP_CAMERA:
+                                       ob_cameras.append(my_object_generic(ob, mtx))
+                       elif tmp_ob_type == 'LAMP':
+#                      elif tmp_ob_type == 'Lamp':
+                               if EXP_LAMP:
+                                       ob_lights.append(my_object_generic(ob, mtx))
+                       elif tmp_ob_type == 'ARMATURE':
+#                      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':
+#                      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':
+#                              if tmp_ob_type != 'Mesh':
+#                                      me = bpy.data.meshes.new()
+                                       try:    me = ob.create_mesh(True, 'PREVIEW')
+#                                      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 = ob.create_mesh(True, 'PREVIEW')
+#                                              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.data
+#                                              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 len(me.uv_textures) > 0:
+#                                      if me.faceUV:
+                                               uvlayer_orig = me.active_uv_texture
+#                                              uvlayer_orig = me.activeUVLayer
+                                               for uvlayer in me.uv_textures:
+#                                              for uvlayer in me.getUVLayerNames():
+#                                                      me.activeUVLayer = uvlayer
+                                                       for f, uf in zip(me.faces, uvlayer.data):
+#                                                      for f in me.faces:
+                                                               tex = uf.image
+#                                                              tex = f.image
+                                                               textures[tex] = texture_mapping_local[tex] = None
+                                                               
+                                                               try: mat = mats[f.material_index]
+#                                                              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 = ob.find_armature()
+                                               blenParentBoneName = None
+                                               
+                                               # parent bone - special case
+                                               if (not armob) and ob.parent and ob.parent.type == 'ARMATURE' and \
+                                                               ob.parent_type == 'BONE':
+#                                              if (not armob) and ob.parent and ob.parent.type == 'Armature' and ob.parentType == Blender.Object.ParentTypes.BONE:
+                                                       armob = ob.parent
+                                                       blenParentBoneName = ob.parent_bone
+#                                                      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 = list(material_mapping_local.keys())
+                                       my_mesh.blenMaterialList = mats
+                                       my_mesh.blenTextures =  list(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 )
+
+               # not forgetting to free dupli_list
+               if ob_base.dupli_list: ob_base.free_dupli_list()
+
+
+       if EXP_ARMATURE:
+               # now we have the meshes, restore the rest arm position
+               for i, arm in enumerate(bpy.data.armatures):
+                       arm.rest_position = ob_arms_orig_rest[i]
+#                      arm.restPosition = ob_arms_orig_rest[i]
+                       
+               if ob_arms_orig_rest:
+                       for ob_base in bpy.data.objects:
+                               if ob_base.type == 'ARMATURE':
+#                              if ob_base.type == 'Armature':
+                                       ob_base.make_display_list()
+#                                      ob_base.makeDisplayList()
+                       # This causes the makeDisplayList command to effect the mesh
+                       sce.set_frame(sce.current_frame)
+#                      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
+               if ob.animation_data:
+                       my_arm.blenAction =     ob.animation_data.action
+               else:
+                       my_arm.blenAction = None
+#              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:
+#              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
+#      for ob in bpy.data.objects:     ob.tag = False
+#      bpy.data.objects.tag = False
+
+       # using a list of object names for tagging (Arystan)
+       tagged_objects = []
+
+       tmp_obmapping = {}
+       for ob_generic in ob_all_typegroups:
+               for ob_base in ob_generic:
+                       tagged_objects.append(ob_base.blenObject.name)
+#                      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.name in tagged_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.name in tagged_objects: # does it exist and is it in the mapping
+#                      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.keys()]
+       textures =      [(sane_texname(tex), tex) for tex in textures.keys()  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.blenObject)
+#                              weights = meshNormalizedWeights(my_mesh.blenData)
+                       
+                       #for bonename, bone, obname, bone_mesh, armob in ob_bones:
+                       for my_bone in ob_bones:
+                               if me in iter(my_bone.blenMeshes.values()):
+                                       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_data
+#      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 = sce.start_frame
+#      start = render.sFrame
+       end =   sce.end_frame
+#      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 = sce.current_frame
+#              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
+
+               # instead of tagging
+               tagged_actions = []
+               
+               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([g.name for g in action.groups]) )
+#                                      action_chan_names = arm_bone_names.intersection( set(action.getChannelNames()) )
+                                       
+                                       if action_chan_names: # at least one channel matches.
+                                               my_arm.blenActionList.append(action)
+                                               tagged_actions.append(action.name)
+#                                              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.name in tagged_actions:
+#                              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))
+
+                               act_start, act_end = blenAction.get_frame_range()
+#                              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:
+                               sce.set_frame(i)
+#                              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 range(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(eulerRadToDeg(prev_eul))
+#                                                                      context_bone_anim_vecs.append(prev_eul)
+                                                       
+                                                       file.write('\n\t\t\t\tChannel: "%s" {' % TX_CHAN) # translation
+                                                       
+                                                       for i in range(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}')
+
+               sce.set_frame(frame_orig)
+#              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:
+               bpy.data.remove_mesh(me)
+#              me.verts = None
+       
+       # --------------------------- Footer
+       if world:
+               m = world.mist
+               has_mist = m.enabled
+#              has_mist = world.mode & 1
+               mist_intense = m.intensity
+               mist_start = m.start
+               mist_end = m.depth
+               mist_height = m.height
+#              mist_intense, mist_start, mist_end, mist_height = world.mist
+               world_hor = world.horizon_color
+#              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 ])
+#              bpy.util.copy_images( [ tex[1] for tex in textures if tex[1] != None ], basepath)       
+       
+       print('export finished in %.4f sec.' % (time.clock() - start_time))
+#      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_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, context):
+       
+       # 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,\
+               context,
+               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()
+
+class EXPORT_OT_fbx(bpy.types.Operator):
+       '''
+       Operator documentation text, will be used for the operator tooltip and python docs.
+       '''
+       __idname__ = "export.fbx"
+       __label__ = "Export FBX"
+       
+       # List of operator properties, the attributes will be assigned
+       # to the class instance from the operator settings before calling.
+       
+       __props__ = [
+               bpy.props.StringProperty(attr="filename", name="File Name", description="File name used for exporting the PLY file", maxlen= 1024, default=""),
+               bpy.props.BoolProperty(attr="EXP_OBS_SELECTED", name="Selected Objects", description="Export selected objects on visible layers", default=True),
+#              bpy.props.BoolProperty(attr="EXP_OBS_SCENE", name="Scene Objects", description="Export all objects in this scene", default=True),
+               bpy.props.FloatProperty(attr="_SCALE", name="Scale", description="Scale all data, (Note! some imports dont support scaled armatures)", min=0.01, max=1000.0, soft_min=0.01, soft_max=1000.0, default=1.0),
+               bpy.props.BoolProperty(attr="_XROT90", name="Rot X90", description="Rotate all objects 90 degrese about the X axis", default=True),
+               bpy.props.BoolProperty(attr="_YROT90", name="Rot Y90", description="Rotate all objects 90 degrese about the Y axis", default=False),
+               bpy.props.BoolProperty(attr="_ZROT90", name="Rot Z90", description="Rotate all objects 90 degrese about the Z axis", default=False),
+               bpy.props.BoolProperty(attr="EXP_EMPTY", name="Empties", description="Export empty objects", default=True),
+               bpy.props.BoolProperty(attr="EXP_CAMERA", name="Cameras", description="Export camera objects", default=True),
+               bpy.props.BoolProperty(attr="EXP_LAMP", name="Lamps", description="Export lamp objects", default=True),
+               bpy.props.BoolProperty(attr="EXP_ARMATURE", name="Armatures", description="Export armature objects", default=True),
+               bpy.props.BoolProperty(attr="EXP_MESH", name="Meshes", description="Export mesh objects", default=True),
+               bpy.props.BoolProperty(attr="EXP_MESH_APPLY_MOD", name="Modifiers", description="Apply modifiers to mesh objects", default=True),
+               bpy.props.BoolProperty(attr="EXP_MESH_HQ_NORMALS", name="HQ Normals", description="Generate high quality normals", default=True),
+               bpy.props.BoolProperty(attr="EXP_IMAGE_COPY", name="Copy Image Files", description="Copy image files to the destination path", default=False),
+               # armature animation
+               bpy.props.BoolProperty(attr="ANIM_ENABLE", name="Enable Animation", description="Export keyframe animation", default=True),
+               bpy.props.BoolProperty(attr="ANIM_OPTIMIZE", name="Optimize Keyframes", description="Remove double keyframes", default=True),
+               bpy.props.FloatProperty(attr="ANIM_OPTIMIZE_PRECISSION", name="Precision", description="Tolerence for comparing double keyframes (higher for greater accuracy)", min=1, max=16, soft_min=1, soft_max=16, default=6.0),
+#              bpy.props.BoolProperty(attr="ANIM_ACTION_ALL", name="Current Action", description="Use actions currently applied to the armatures (use scene start/end frame)", default=True),
+               bpy.props.BoolProperty(attr="ANIM_ACTION_ALL", name="All Actions", description="Use all actions for armatures, if false, use current action", default=False),
+               # batch
+               bpy.props.BoolProperty(attr="BATCH_ENABLE", name="Enable Batch", description="Automate exporting multiple scenes or groups to files", default=False),
+               bpy.props.BoolProperty(attr="BATCH_GROUP", name="Group > File", description="Export each group as an FBX file, if false, export each scene as an FBX file", default=False),
+               bpy.props.BoolProperty(attr="BATCH_OWN_DIR", name="Own Dir", description="Create a dir for each exported file", default=True),
+               bpy.props.StringProperty(attr="BATCH_FILE_PREFIX", name="Prefix", description="Prefix each file with this name", maxlen= 1024, default=""),
+       ]
+       
+       def poll(self, context):
+               print("Poll")
+               return context.active_object != None
+       
+       def execute(self, context):
+               if not self.filename:
+                       raise Exception("filename not set")
+
+               GLOBAL_MATRIX = mtx4_identity
+               GLOBAL_MATRIX[0][0] = GLOBAL_MATRIX[1][1] = GLOBAL_MATRIX[2][2] = self._SCALE
+               if self._XROT90: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_x90n
+               if self._YROT90: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_y90n
+               if self._ZROT90: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_z90n
+                       
+               write(self.filename,
+                         None, # XXX
+                         context,
+                         self.EXP_OBS_SELECTED,
+                         self.EXP_MESH,
+                         self.EXP_MESH_APPLY_MOD,
+#                        self.EXP_MESH_HQ_NORMALS,
+                         self.EXP_ARMATURE,
+                         self.EXP_LAMP,
+                         self.EXP_CAMERA,
+                         self.EXP_EMPTY,
+                         self.EXP_IMAGE_COPY,
+                         GLOBAL_MATRIX,
+                         self.ANIM_ENABLE,
+                         self.ANIM_OPTIMIZE,
+                         self.ANIM_OPTIMIZE_PRECISSION,
+                         self.ANIM_ACTION_ALL,
+                         self.BATCH_ENABLE,
+                         self.BATCH_GROUP,
+                         self.BATCH_FILE_PREFIX,
+                         self.BATCH_OWN_DIR)           
+
+               return ('FINISHED',)
+       
+       def invoke(self, context, event):       
+               wm = context.manager
+               wm.add_fileselect(self.__operator__)
+               return ('RUNNING_MODAL',)
+
+
+bpy.ops.add(EXPORT_OT_fbx)
+
+# if __name__ == "__main__":
+#      bpy.ops.EXPORT_OT_ply(filename="/tmp/test.ply")
+
+
+# NOTES (all line numbers correspond to original export_fbx.py (under release/scripts)
+# - Draw.PupMenu alternative in 2.5?, temporarily replaced PupMenu with print
+# - get rid of cleanName somehow
+# + fixed: isinstance(inst, bpy.types.*) doesn't work on RNA objects: line 565
+# + get rid of BPyObject_getObjectArmature, move it in RNA?
+# - BATCH_ENABLE and BATCH_GROUP options: line 327
+# - implement all BPyMesh_* used here with RNA
+# - getDerivedObjects is not fully replicated with .dupli* funcs
+# - talk to Campbell, this code won't work? lines 1867-1875
+# - don't know what those colbits are, do we need them? they're said to be deprecated in DNA_object_types.h: 1886-1893
+# - no hq normals: 1900-1901
+
+# TODO
+
+# - bpy.data.remove_scene: line 366
+# - bpy.sys.time move to bpy.sys.util?
+# - new scene creation, activation: lines 327-342, 368
+# - uses bpy.sys.expandpath, *.relpath - replace at least relpath
+
+# SMALL or COSMETICAL
+# - find a way to get blender version, and put it in bpy.util?, old was Blender.Get('version')
diff --git a/release/io/export_obj.py b/release/io/export_obj.py
new file mode 100644 (file)
index 0000000..b2d4af7
--- /dev/null
@@ -0,0 +1,988 @@
+#!BPY
+
+"""
+Name: 'Wavefront (.obj)...'
+Blender: 248
+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.21"
+
+__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.
+"""
+
+
+# --------------------------------------------------------------------------
+# OBJ Export v1.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 *****
+# --------------------------------------------------------------------------
+
+# import math and other in functions that use them for the sake of fast Blender startup
+# import math
+import os
+
+import bpy
+import Mathutils
+
+
+# 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(' ', '_')
+
+
+# this used to be in BPySys module
+# frankly, I don't understand how it works
+def BPySys_cleanName(name):
+
+       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,40,41,42,43,44,46,47,58,59,60,61,62,63,64,91,92,93,94,96,123,124,125,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])
+
+       for ch in invalid:
+               name = name.replace(ch, '_')
+       return name
+
+# A Dict of Materials
+# (material.name, image.name):matname_imagename # matname_imagename has gaps removed.
+MTL_DICT = {}  
+
+def write_mtl(scene, filename, copy_images):
+
+       world = scene.world
+       worldAmb = world.ambient_color
+
+       dest_dir = os.path.dirname(filename)
+
+       def copy_image(image):
+               rel = image.get_export_path(dest_dir, True)
+
+               if copy_images:
+                       abspath = image.get_export_path(dest_dir, False)
+                       if not os.path.exists(abs_path):
+                               shutil.copy(bpy.sys.expandpath(image.filename), abs_path)
+
+               return rel
+
+
+       file = open(filename, "w")
+       # XXX
+#      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.items():
+               
+               # 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.specular_hardness-1) * 1.9607843137254901) ) # Hardness, convert blenders 1-511 to MTL's
+                       file.write('Ka %.6f %.6f %.6f\n' %      tuple([c*mat.ambient for c in worldAmb])  ) # Ambient, uses mirror colour,
+                       file.write('Kd %.6f %.6f %.6f\n' % tuple([c*mat.diffuse_reflection for c in mat.diffuse_color]) ) # Diffuse
+                       file.write('Ks %.6f %.6f %.6f\n' % tuple([c*mat.specular_reflection for c in mat.specular_color]) ) # Specular
+                       if hasattr(mat, "ior"):
+                               file.write('Ni %.6f\n' % mat.ior) # Refraction index
+                       else:
+                               file.write('Ni %.6f\n' % 1.0)
+                       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.shadeless:
+                               file.write('illum 0\n') # ignore lighting
+                       elif mat.specular_reflection == 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!
+                       # write relative image path
+                       rel = copy_image(img)
+                       file.write('map_Kd %s\n' % rel) # Diffuse mapping image
+#                      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.textures:
+                               if mtex and mtex.texure.type == 'IMAGE':
+                                       try:
+                                               filename = copy_image(mtex.texture.image)
+#                                              filename = mtex.texture.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()
+
+# XXX not used
+def copy_file(source, dest):
+       file = open(source, 'rb')
+       data = file.read()
+       file.close()
+       
+       file = open(dest, 'wb')
+       file.write(data)
+       file.close()
+
+
+# XXX not used
+def copy_images(dest_dir):
+       if dest_dir[-1] != os.sep:
+               dest_dir += os.sep
+#      if dest_dir[-1] != sys.sep:
+#              dest_dir += sys.sep
+       
+       # Get unique image names
+       uniqueImages = {}
+       for matname, mat, image in MTL_DICT.values(): # 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.textures:
+                               if mtex and mtex.texture.type == 'IMAGE':
+                                       image_tex = mtex.texture.image
+                                       if image_tex:
+                                               try:
+                                                       uniqueImages[image_tex] = image_tex
+                                               except:
+                                                       pass
+       
+       # Now copy images
+       copyCount = 0
+       
+#      for bImage in uniqueImages.values():
+#              image_path = bpy.sys.expandpath(bImage.filename)
+#              if bpy.sys.exists(image_path):
+#                      # Make a name for the target path.
+#                      dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1]
+#                      if not bpy.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
+
+#      paths= bpy.util.copy_images(uniqueImages.values(), dest_dir)
+
+       print('\tCopied %d images' % copyCount)
+#      print('\tCopied %d images' % copyCount)
+
+# XXX not converted
+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
+
+
+# XXX not converted
+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 range(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 range(tot_parm)]
+               
+               if do_endpoints: # end points, force param
+                       for i in range(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, scene,
+                 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.
+       '''
+
+       # XXX
+       import math
+       
+       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_index in face.verts:
+#              for vert in face:
+                       vWeights = vWeightMap[vert_index]
+#                      vWeights = vWeightMap[vert]
+                       for vGroupName, weight in vWeights:
+                               weightDict[vGroupName] = weightDict.get(vGroupName, 0) + weight
+               
+               if weightDict:
+                       alist = [(weight,vGroupName) for vGroupName, weight in weightDict.items()] # sort least to greatest amount of weight
+                       alist.sort()
+                       return(alist[-1][1]) # highest value last
+               else:
+                       return '(null)'
+
+       # TODO: implement this in C? dunno how it should be called...
+       def getVertsFromGroup(me, group_index):
+               ret = []
+
+               for i, v in enumerate(me.verts):
+                       for g in v.groups:
+                               if g.group == group_index:
+                                       ret.append((i, g.weight))
+
+               return ret
+
+
+       print('OBJ Export path: "%s"' % filename)
+       temp_mesh_name = '~tmp-mesh'
+
+       time1 = bpy.sys.time()
+#      time1 = sys.time()
+#      scn = Scene.GetCurrent()
+
+       file = open(filename, "w")
+       
+       # Write Header
+       version = "2.5"
+       file.write('# Blender3D v%s OBJ File: %s\n' % (version, bpy.data.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] ))
+       
+       if EXPORT_ROTX90:
+               mat_xrot90= Mathutils.RotationMatrix(-math.pi/2, 4, 'x')
+               
+       # Initialize totals, these are updated each object
+       totverts = totuvco = totno = 1
+       
+       face_vert_index = 1
+       
+       globalNormals = {}
+
+       # Get all meshes
+       for ob_main in objects:
+
+               # ignore dupli children
+               if ob_main.parent and ob_main.parent.dupli_type != 'NONE':
+                       # XXX
+                       print(ob_main.name, 'is a dupli child - ignoring')
+                       continue
+
+               obs = []
+               if ob_main.dupli_type != 'NONE':
+                       # XXX
+                       print('creating dupli_list on', ob_main.name)
+                       ob_main.create_dupli_list()
+                       
+                       obs = [(dob.object, dob.matrix) for dob in ob_main.dupli_list]
+
+                       # XXX debug print
+                       print(ob_main.name, 'has', len(obs), 'dupli children')
+               else:
+                       obs = [(ob_main, ob_main.matrix)]
+
+               for ob, ob_mat in obs:
+
+                       # XXX postponed
+#                      # 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
+
+                       if ob.type != 'MESH':
+                               continue
+
+                       me = ob.create_mesh(EXPORT_APPLY_MODIFIERS, 'PREVIEW')
+
+                       if EXPORT_ROTX90:
+                               me.transform(ob_mat * mat_xrot90)
+                       else:
+                               me.transform(ob_mat)
+
+#                      # Will work for non meshes now! :)
+#                      me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, EXPORT_POLYGROUPS, scn)
+#                      if not me:
+#                              continue
+
+                       if EXPORT_UV:
+                               faceuv = len(me.uv_textures) > 0
+                       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 f.verts[3] != 0:
+                                               has_quads = True
+                                               break
+                               
+                               if has_quads:
+                                       newob = bpy.data.add_object('MESH', 'temp_object')
+                                       newob.data = me
+                                       # if we forget to set Object.data - crash
+                                       scene.add_object(newob)
+                                       newob.convert_to_triface(scene)
+                                       # mesh will still be there
+                                       scene.remove_object(newob)
+
+                       # Make our own list so it can be sorted to reduce context switching
+                       face_index_pairs = [ (face, index) for index, face in enumerate(me.faces)]
+                       # faces = [ f for f in me.faces ]
+                       
+                       if EXPORT_EDGES:
+                               edges = me.edges
+                       else:
+                               edges = []
+
+                       if not (len(face_index_pairs)+len(edges)+len(me.verts)): # Make sure there is somthing to write                         
+
+                               # clean up
+                               bpy.data.remove_mesh(me)
+
+                               continue # dont bother with this mesh.
+                       
+                       # XXX
+                       # High Quality Normals
+                       if EXPORT_NORMALS and face_index_pairs:
+                               me.calc_normals()
+#                              if EXPORT_NORMALS_HQ:
+#                                      BPyMesh.meshCalcNormals(me)
+#                              else:
+#                                      # transforming normals is incorrect
+#                                      # when the matrix is scaled,
+#                                      # better to recalculate them
+#                                      me.calcNormals()
+                       
+                       materials = me.materials
+                       
+                       materialNames = []
+                       materialItems = [m for m in 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:
+                               # XXX update
+                               tface = me.active_uv_texture.data
+
+                               # exception only raised if Python 2.3 or lower...
+                               try:
+                                       face_index_pairs.sort(key = lambda a: (a[0].material_index, tface[a[1]].image, a[0].smooth))
+                               except:
+                                       face_index_pairs.sort(lambda a,b: cmp((a[0].material_index, tface[a[1]].image, a[0].smooth),
+                                                                                                                         (b[0].material_index, tface[b[1]].image, b[0].smooth)))
+                       elif len(materials) > 1:
+                               try:
+                                       face_index_pairs.sort(key = lambda a: (a[0].material_index, a[0].smooth))
+                               except:
+                                       face_index_pairs.sort(lambda a,b: cmp((a[0].material_index, a[0].smooth),
+                                                                                                                         (b[0].material_index, b[0].smooth)))
+                       else:
+                               # no materials
+                               try:
+                                       face_index_pairs.sort(key = lambda a: a[0].smooth)
+                               except:
+                                       face_index_pairs.sort(lambda a,b: cmp(a[0].smooth, b[0].smooth))
+#                      if EXPORT_KEEP_VERT_ORDER:
+#                              pass
+#                      elif faceuv:
+#                              try:    faces.sort(key = lambda a: (a.mat, a.image, a.smooth))
+#