Script from Gerhard for exporting blender to m3g and java source,
authorCampbell Barton <ideasman42@gmail.com>
Tue, 29 May 2007 21:39:29 +0000 (21:39 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Tue, 29 May 2007 21:39:29 +0000 (21:39 +0000)
see website for examples
http://www.nelson-games.de/bl2m3g/default.html

release/scripts/export_m3g.py [new file with mode: 0644]

diff --git a/release/scripts/export_m3g.py b/release/scripts/export_m3g.py
new file mode 100644 (file)
index 0000000..afb019f
--- /dev/null
@@ -0,0 +1,3047 @@
+#!BPY
+""" Registration info for Blender menus:
+Name: 'M3G (.m3g, .java)...'
+Blender: 244
+Group: 'Export'
+Tooltip: 'Export to M3G'
+"""
+#------------------------------------------------------------------------
+# M3G exporter for blender 2.37 or above
+#
+# Source: http://www.nelson-games.de/bl2m3g/source
+#
+# $Id: m3g_export.py,v 0.1 2005/04/19 12:25 gerhardv Exp gerhardv $
+#
+# Author: Gerhard Völkl
+#
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# Copyright (C) 2005: gerhard völkl gkvoelkl@yahoo.de
+#
+# 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 *****
+#
+# To use script:
+# 1.) load this file in the text window.
+#     (press SHIFT+F11, Open New via Datablock button)
+# 2.) make sure your mouse is over the text edit window and
+#     run this script. (press ALT+P)
+# Or:
+#   copy to the scripts directory and it will appear in the
+#   export list. (Needs 2.32 or higher)
+#
+# Based on informations from:
+#   wrl2export.py from Rick Kimball and others
+# --------------------------------------------------------------------------#
+# History 0.2
+# * maximal Precision in VertexArray (with algorithms from Kalle Raita)
+# * IPO Animation with mesh: Rotation, Translation and Size
+# History 0.3
+# * to find a 3d object in your java programm you can assign a userID
+#   your blender object has name 'cube#01' your 3d object will have ID 01
+#   the number after '#' is taken
+# * more than one material per mesh can be used
+# * uv texture support (implemented by Aki Koskinen and Juha Laitinen)
+#   The image which is bound to the faces will be exportet within m3g-file
+#   Limitations by M3G-API:
+#   The width and height of the image must be non-negative powers of two, 
+#   but they need not to be equal. Maximum value is 256.
+#   *.java export: Only PNG images can be used.
+# History 0.4
+# * check limitation of texture images (credit to MASTER_ZION for Brasil)
+# * Better light: The light modeles of Blender and M3G are naturally 
+#   different. So the export script trys to translate as much as possible
+#
+#   M3G Light type          Blender Light type
+#   --------------------------------------------------------------
+#   AMIENT Light            Not available as light type in Blender
+#   DIRECTIONAL Light       SUN
+#   OMNIdirectional light   LAMP
+#   SPOT light              SPOT
+#   not translated          HEMI
+#   not translated          AREA
+#
+#   Attributs of M3G Lights:
+#
+#   Attenuation (OMNI,SPOT):
+#     Intensity of light changes with distance
+#     The attenuation factor is 1 / (c + l d + q d2)
+#     where d is the distance between the light and the vertex being lighted
+#     and c, l, q are the constant, linear, and quadratic coefficients.
+#     In Blender exists much complex posibilies. To simplify exporter uses
+#     only button Dist: distance at which the light intensity is half 
+#     the Energy 
+#   Color (ALL)
+#     Color of light 
+#   Intensity (ALL)
+#     The RGB color of this Light is multiplied component-wise with the 
+#     intensity. In Blender : energy
+#   SpotAngle (SPOT)
+#     the spot cone angle for this Light
+#     In Blender: spotSize
+#   SpotExponent (SPOT)
+#     The spot exponent controls the distribution of the intensity of 
+#     this Light within the spot cone, such that larger values yield 
+#     a more concentrated cone. In Blender: SpotBl 
+#
+# * Some GUI for options
+#   First prototype of GUI was created using RipSting's Blender-Python 
+#   GUI designer. Download at Http://oregonstate.edu/~dennisa/Blender/BPG/
+#
+# * Ambiente light
+#   Information is taken by world ambiente attribute
+#
+# * Parenting Part 1
+#   In Blender the Empty object is used to group objects. All objects
+#   which have the same empty as parent are the member of the same group.
+#   
+#   empty <-- Parent of -- element 1
+#        <-- Parent of -- element 2
+#
+#       is translated in M3G
+#
+#   group-Node -- Member --> element 1
+#              -- Member --> element 2
+#
+#   In Blender every object can be the parent of every other object
+#   In M3G that is not possible. Only a group object can be parent.
+#   (Or the world object which is derived from group).
+#   That will come later as Parenting Part 2
+#
+# * Backface Culling
+#   you can use backface culling, if option "use backface culloing" is on.
+#   Culling will be set in PolygonMode object of every mesh. The correct
+#   winding is controlled.
+# History 0.5
+#* Bone Animation - Armature (Part 1)
+#
+#  Armature is the skeleton for skinned meshes. It stores the bones in 
+#  rest position (more information http://www.blender.org/cms/How_Armatures_work.634.0.html)
+#  You can work in Blender with bones and meshes in different ways. In
+#  this first attempt only the use of vertex groups is assisted.
+#
+#  Blender-Objekts       translated into      M3G-Objects
+#
+#      MESH                                  SkinnedMesh
+#        |                                       |
+#        v                                       v
+#     ARMATURE                                 Group
+#        |                                       |
+#        v                                       v
+#      BONE_1                                  Group
+#                                              Group_second
+#        |                                       |
+#        V                                       v
+#      BONE_2                                  Group
+#                                              Group_secound
+#
+#  Every bone is translated into two groups at the moment, because
+#  the second bone is needed to do the animation in an easy way.
+#
+#  The animations in Blender for meshes are stored in action objects.
+# 
+#  Blender Objects      translated into      M3G-Objects
+#
+#   ARMATURE
+#       | activ
+#       v
+#    ACTION                               ANIMATIONCONTROLLER
+#       | 1..n                                    ^
+#       v                                 ANIMATIONTRACK  --> Group_second
+#     IPOs                                        |
+#                                                 v
+#                                            KEYSEQUENZE
+#
+#  One action is translated into one animationcontroller. One IPO is 
+#  translated in one KEYSEQUENZE and one ANIMATIONTRACK.
+#
+#  At the moment only the active action of the armature object is translated.
+#
+#* Print Info, if type of light is used that is not supported
+#
+# History 0.5
+#
+#* New Option exportAllAction (default value: false)
+#  If that option is true, all actions will be exported - not only the active
+#  action. 
+#  At the moment you can only assign one action to one armature.
+#  To know which action is used with which armature the action
+#  needs a special name : 
+#        <Action Name>#A<M3G ID of Armature>E<End Frame>#<ID of Action>
+
+#  Example: Name of action : walk#A10E250#02
+#           Name of armature : man#10
+#           End Frame: 250
+# 
+# History 0.6
+# Include the same image only one time into the m3g-file
+#
+# All the following changes of this version was made by Claus Hoefele
+#
+#* Until now all vertices of the faces was been written.
+#  Now the vertices will be used again if possible: 
+#     normal and texture coordinates of to vertices have to be the same
+#
+#* Smooth/solid shading can now be defined for every single material:
+#     in Editing panel (F9)>Link and Materials
+#
+#* This script uses now correctly the TexFace and Shadless Buttons in
+#  Shading panel (F5)>Material buttons>Material box. 
+#  TexFace switches on/off the Export of texture coordinates.
+#  Shadeless does the some with the normal coordinates
+#
+#* The GUI was redesigned in a PupBlock
+#  
+#* Options:
+#
+#** Texturing Enabled: Switches on/off export of textures and texture 
+#           coordinates. Attention: the TextFace button switches only
+#           for one mesh
+#** Texturing External: the textures will be included it mg3-file or
+#           exported in seperate file
+#** Lighting Enabled: turns on/off export of lights and normal completly
+#           Attention: Shadeless only for one mesh
+#** Persp. Correction: turns on/off perspective correction in PolygonMode.
+#** Smooth Shading: turns on/off smooth shading in PolygonMode.
+#
+#* Textures in external references are used again (with ImageFactory)
+#
+#* Blender function: Double Sided button in Editing Context
+#                    (F9)>Mesh panel)
+#  turn on/off PolygonMode.CULL_BACK anzuschalten.
+#
+#* Script ingnores meshes that have no faces
+#
+# History 0.7
+#
+# * Exporter can work with texture coordinates greater 1 and smaller 0
+#
+# * Adler32 did not work always correct. New implementation made.
+#
+# * Modul shutil is not needed any longer. Exporter has its own copy_file.
+#   (realized and inspired by ideasman_42 and Martin Neumann)
+# --------------------------------------------------------------------------#
+# TODO: Export only selected mesh
+# TODO: Optimize Bones <--> Vertex Group mapping
+# TODO: Compressed File
+# TODO: MTex - Support
+# TODO: By Rotating use SQUAD instead of Beziere. It's smoother
+import Blender
+from Blender import Types,Lamp,Material,Texture,Window,Registry,Draw
+from Blender.BGL import *
+from Blender.Object import *
+from Blender.Camera import *
+from Blender.Mesh import *
+from array import array
+import sys, struct, zlib
+from inspect import *
+from types import *
+from Blender.Mathutils import *
+from os.path import *
+#import rpdb2
+
+# ---- Helper Functions -------------------------------------------------------#
+def copy_file(source, dest):
+       file = open(source, 'rb')
+       data = file.read()
+       file.close()
+       
+       file = open(dest, 'wb')
+       file.write(data)
+       file.close()
+
+def tracer(frame, event, arg):
+    '''Global trace function'''
+    if event=='call':
+        tmp = getargvalues(frame)
+        print event, frame.f_code.co_name, frame.f_lineno, \
+                     formatargvalues(tmp[0],tmp[1],tmp[2],tmp[3])
+    elif event=='line':
+        print event, frame.f_code.co_name, frame.f_lineno
+        #print event, frame.f_code.co_name, frame.f_lineno, \
+        #             getsourcelines(frame.f_code)[frame.f_lineno]
+    elif event=='return':
+        print event, frame.f_code.co_name, frame.f_lineno, "->", arg
+    return tracer
+
+def doSearchDeep(inList,outList):
+    '''Does deepsearch for all elements in inList'''
+    for element in inList:
+        if element != None : outList = element.searchDeep(outList)
+    return outList
+
+
+def getId(aObject):
+    ''' returns 0 if Object is None: M3G value for null'''
+    if aObject == None: return 0
+    return aObject.id
+def toJavaBoolean(aValue):
+    ''' returns java equivalent to boolean'''
+    if aValue:
+        return 'true'
+    else :
+        return 'false'
+
+def sign(a):
+    if a<0 : return -1
+    elif a>0 : return 1
+    else : return 0
+     
+def isOrderClockWise(v,normal):
+    ''' returns true, if order of vertices is clockwise. Important for 
+        culling '''
+    # (v2-v0)x(v2-v1)=surface_normal
+    #
+    if type(v[0]) is Types.MVertType:
+        mNormal = TriangleNormal(Vector(v[0].co),Vector(v[1].co),Vector(v[2].co))
+    else:
+        mNormal = TriangleNormal(Vector(v[0]),Vectot(v[1]),Vector(v[2]))
+    #print "normal ",mNormal.normalize()
+    #print "BNormal ",normal.normalize()
+    
+    # Do not use any longer. Blender does it correct
+    
+    result = (sign(normal.x)==sign(mNormal.x) and
+              sign(normal.y)==sign(mNormal.y) and
+              sign(normal.z)==sign(mNormal.z))
+    #print "Result ",result
+    
+    return True
+    
+    
+# ---- M3G Types --------------------------------------------------------------#
+class M3GVertexList:
+    def __init__(self, wrapList):
+        self.mlist = wrapList
+
+    def __getitem__(self, key):
+        item = self.mlist[key]
+        if type(item) is Types.MVertType:
+            result =(item.co[0],item.co[1],item.co[2])
+        else:
+            result = item
+        return result
+
+class M3GBoneReference:
+    def __init__(self,first,count):
+        self.firstVertex=first #UInt32 
+        self.vertexCount=count #UInt32 
+        
+        
+class M3GBone:
+    def __init__(self):
+        self.verts=[] #List of influenced verts
+        self.transformNode=None #ObjectIndex
+        self.references = [] #References to Verts that are needed
+        self.weight=0      #Int32
+
+    
+    def setVerts(self,aVerts):
+        self.verts = aVerts
+        self.createReferences()
+        
+    def createReferences(self):
+        #print "createReference::len(verts) ",len(self.verts)
+        if len(self.verts)==0: return #No Verts available
+        self.verts.sort()
+        ref = []
+        list = []
+        last = self.verts[0]-1
+        count = 0
+        for vert in self.verts:
+            #print "vert ",vert
+            if vert==last+1:
+                list.append(vert)
+            else:
+                ref.append(M3GBoneReference(list[0],len(list)))
+                #print list[0],len(list)
+                list=[vert]
+            last=vert
+            #print "list ",list
+        if len(list)>0:
+            ref.append(M3GBoneReference(list[0],len(list)))
+        self.references = ref
+        
+    
+class M3GVector3D:
+    def __init__(self,ax=0.0,ay=0.0,az=0.0):
+        self.x = ax #Float32
+        self.y = ay #Float32
+        self.z = az #Float32
+    
+    def writeJava(self):
+        return str(self.x)+"f, "+str(self.y)+"f, "+str(self.z)+"f"
+    
+    def getData(self):
+        return struct.pack("<3f",self.x,self.y,self.z)
+    
+    def getDataLength(self):
+        return struct.calcsize("<3f")
+
+class M3GMatrix:
+    """ A 4x4 generalized matrix. The 16 elements of the
+        matrix are output in the same order as they are
+        retrieved using the API Transform.get method. In
+        other words, in this order:
+        0 1 2 3
+        4 5 6 7
+        8 9 10 11
+        12 13 14 15 """
+    def __init__(self):
+        self.elements=16 * [0.0] #Float32
+        
+    def identity(self):
+        self.elements[ 0] = 1.0
+        self.elements[ 5] = 1.0
+        self.elements[10] = 1.0
+        self.elements[15] = 1.0
+    
+    def getData(self):
+        return struct.pack('<16f',self.elements[0],self.elements[1],
+        self.elements[2],self.elements[3],
+        self.elements[4],self.elements[5],
+        self.elements[6],self.elements[7],
+        self.elements[8],self.elements[9],
+        self.elements[10],self.elements[11],
+        self.elements[12],self.elements[13],
+        self.elements[14],self.elements[15])
+
+    def getDataLength(self):
+        return struct.calcsize('<16f')
+        
+        
+class M3GColorRGB:
+    """ A color, with no alpha information. Each compo-
+        nent is scaled so that 0x00 is 0.0, and 0xFF is 1.0.
+        """
+    def __init__(self,ared=0,agreen=0,ablue=0):
+        self.red = ared #Byte
+        self.green = agreen #Byte
+        self.blue = ablue #Byte
+        
+    def writeJava(self):
+        return "0x"+("%02X%02X%02X%02X" % (0.0, self.red, self.green, self.blue))
+    
+    def getData(self):
+        return struct.pack('3B',self.red,self.green,self.blue)
+    
+    def getDataLength(self):
+        return struct.calcsize('3B')
+
+
+class M3GColorRGBA:
+    """ A color, with alpha information. Each component
+        is scaled so that 0x00 is 0.0, and 0xFF is 1.0. The
+        alpha value is scaled so that 0x00 is completely
+        transparent, and 0xFF is completely opaque. 
+        """
+    def __init__(self,ared=0,agreen=0,ablue=0,aalpha=0):
+        self.red = ared #Byte 
+        self.green = agreen #Byte 
+        self.blue = ablue #Byte 
+        self.alpha = aalpha #Byte
+
+    def writeJava(self):
+        return "0x"+("%02X%02X%02X%02X" % (self.alpha, self.red, self.green, self.blue))
+        
+    def getData(self):
+        return struct.pack('4B',self.red,self.green,self.blue,self.alpha)
+    
+    def getDataLength(self):
+        return struct.calcsize('4B')
+    
+    
+#ObjectIndex
+#The index of a previously encountered object in
+#the file. Although this is serialized as a single
+#unsigned integer, it is included in the compound
+#type list because of the additional semantic infor-
+#mation embodied in its type. A value of 0 is
+#reserved to indicate a null reference; actual object indices start from 1. Object indices must refer
+#only to null or to an object which has already been
+#created during the input deserialization of a file -
+#they must be less than or equal to the index of the
+#object in which they appear. Other values are dis-
+#allowed and must be treated as errors.
+#UInt32
+#index;
+
+# ---- M3G Proxy --------------------------------------------------------------- #
+class M3GProxy:
+    def __init__(self):
+        self.name = ""
+        self.id=0
+        self.ObjectType=0
+        self.binaryFormat=''
+        
+    def __repr__(self):
+        return "<"+str(self.__class__)[9:] + ":" + str(self.name) + ":" + str(self.id) + ">"
+
+        
+class M3GHeaderObject(M3GProxy):
+    def __init__(self):
+        M3GProxy.__init__(self)
+        self.M3GHeaderObject_binaryFormat = '<BBBII'
+        self.ObjectType=0
+        self.id = 1   #Special Object: always 1
+        self.VersionNumber=[1,0] #Byte[2] 
+        self.hasExternalReferences=False #Boolean External Files needed? eg. Textures
+        self.TotalFileSize=0 #UInt32 
+        self.ApproximateContentSize=0 #UInt32 Only a hint! External sources included
+        self.AuthoringField='Blender M3G Export' #String 
+        
+    def getData(self):
+        data = struct.pack(self.M3GHeaderObject_binaryFormat,
+                           self.VersionNumber[0],
+                           self.VersionNumber[1],
+                           self.hasExternalReferences,
+                           self.TotalFileSize,
+                           self.ApproximateContentSize)
+        data += struct.pack(str(len(self.AuthoringField)+1)+'s',self.AuthoringField)
+        return data
+    
+    def getDataLength(self):
+        value = struct.calcsize(self.M3GHeaderObject_binaryFormat)
+        return value + struct.calcsize(str(len(self.AuthoringField)+1)+'s')
+        
+class M3GExternalReference(M3GProxy):
+    def __init__(self):         
+        M3GProxy.__init__(self)
+        self.ObjectType=0xFF
+        self.URI=''             #reference URI
+        
+    def getData(self):
+        data = struct.pack(str(len(self.URI)+1) + 's', self.URI)
+        return data
+        
+    def getDataLength(self):
+        return struct.calcsize(str(len(self.URI)+1)+'s')
+        
+    def searchDeep(self,alist):
+        if not(self in alist): alist.append(self)
+        return alist
+        
+    def __repr__(self):
+        return M3GProxy.__repr__(self) + " (" + self.URI + ")"
+       
+        
+class M3GObject3D(M3GProxy):
+    def __init__(self):
+        M3GProxy.__init__(self)
+        self.userID=0 #UInt32 - field may be any value
+        self.animationTracks=[] #ObjectIndex[] 
+        self.userParameterCount=0 #UInt32  - No user parameter used 
+        
+    def searchDeep(self,alist):
+        alist = doSearchDeep(self.animationTracks,alist)
+        if not(self in alist): alist.append(self)
+        return alist
+        
+    def getData(self):
+        data = struct.pack('<I',self.userID)
+        print "write userID",self.userID,self.name,str(self), self.getDataLength()
+        data += struct.pack('<I',len(self.animationTracks))
+        for element in self.animationTracks:
+            data += struct.pack('<I',getId(element))
+        data += struct.pack('<I',self.userParameterCount)
+        return data
+
+    def getDataLength(self):
+        value = struct.calcsize('<3I')
+        if len(self.animationTracks) > 0: 
+            value += struct.calcsize('<'+str(len(self.animationTracks))+'I')
+        return value
+
+    def writeJava(self,aWriter,aCreate):
+        if aCreate : pass #Abstract! Could not be created
+        if len(self.animationTracks) > 0 :
+            aWriter.write(2)
+            for iTrack in self.animationTracks:
+                aWriter.write(2,"BL%i.addAnimationTrack(BL%i);" % (self.id,iTrack.id))
+            
+            
+class M3GTransformable(M3GObject3D):
+    def __init__(self):
+        M3GObject3D.__init__(self)
+        self.hasComponentTransform=False #Boolean 
+        #IF hasComponentTransform==TRUE, THEN
+        self.translation=M3GVector3D(0,0,0) #Vector3D 
+        self.scale=M3GVector3D(1,1,1) #Vector3D 
+        self.orientationAngle=0 #Float32 
+        self.orientationAxis=M3GVector3D(0,0,0) #Vector3D undefined
+        #END
+        self.hasGeneralTransform=False #Boolean 
+        #IF hasGeneralTransform==TRUE, THEN
+        self.transform = M3GMatrix() #Matrix identity
+        self.transform.identity()
+        #END
+        #If either hasComponentTransform or hasGeneralTransform is false, the omitted fields will be
+        #initialized to their default values (equivalent to an identity transform in both cases).
+
+    def writeJava(self,aWriter,aCreate):
+        if aCreate: pass #Abstract Base Class! Cant't be created
+        M3GObject3D.writeJava(self,aWriter,False)
+        if self.hasGeneralTransform :
+            aWriter.write(2,"float[] BL%i_matrix = {" % (self.id))
+            aWriter.writeList(self.transform.elements,4,"f")
+            aWriter.write(2,"};")
+            aWriter.write(2)
+            aWriter.write(2,"Transform BL%i_transform = new Transform();" % (self.id))
+            aWriter.write(2,"BL%i_transform.set(BL%i_matrix);" % (self.id,self.id))
+            aWriter.write(2,"BL%i.setTransform(BL%i_transform);" % (self.id,self.id))
+            aWriter.write(2)
+        if self.hasComponentTransform:
+            aWriter.write(2,("BL%i.setTranslation("+self.translation.writeJava()+");")
+                             %(self.id))
+    
+    def getData(self):
+        data = M3GObject3D.getData(self)
+        data += struct.pack("<B",self.hasComponentTransform) 
+        if self.hasComponentTransform==True:
+            data += self.translation.getData()
+            data += self.scale.getData() 
+            data += struct.pack('<f',self.orientationAngle) 
+            data += self.orientationAxis.getData()
+        data += struct.pack("<B",self.hasGeneralTransform) 
+        if self.hasGeneralTransform==True:
+            data += self.transform.getData()
+        return data
+        
+    def getDataLength(self):
+        value = M3GObject3D.getDataLength(self)
+        value += struct.calcsize("<B")
+        if self.hasComponentTransform==True:
+            value += self.translation.getDataLength() 
+            value += self.scale.getDataLength() 
+            value += struct.calcsize('<f') 
+            value += self.orientationAxis.getDataLength()
+        value += struct.calcsize("<B") 
+        if self.hasGeneralTransform==True:
+            value += self.transform.getDataLength()
+        return value
+        
+        
+class M3GNode(M3GTransformable):
+    def __init__(self):
+        M3GTransformable.__init__(self)
+        self.blenderObj = None #Pointer to corrsponding BlenderObj 
+        self.parentBlenderObj = None #Pointer to Parent in Blender
+        self.blenderMatrixWorld = None #BlenderObj matrixWorld
+        self.M3GNode_binaryFormat = '<BBBIB'
+        self.enableRendering=True #Boolean 
+        self.enablePicking=True #Boolean 
+        self.alphaFactor=255 #Byte 0x00 is equivalent to 0.0 (fully transparent), and 255 is equivalent to 1.0 (fully opaque);
+        self.scope=4294967295 #-1 #UInt32 
+        self.hasAlignment = False #Boolean 
+        #IF hasAlignment==TRUE, THEN
+        self.M3GNode_binaryFormat_2 = '<BBII'
+        self.zTarget=0 #Byte   The zTarget and yTarget fields must each hold a valid enumerated value, 
+        self.yTarget=0 #Byte   as specified in the class definition. Other values must be treated as errors.
+        self.zReference=None #ObjectIndex 
+        self.yReference=None #ObjectIndex 
+        #END
+        #If the hasAlignment field is false, the omitted fields are initialized to their default values.
+
+
+    def getData(self):
+        data = M3GTransformable.getData(self)
+        #print "Binary ",self.binaryFormat
+        data += struct.pack(self.M3GNode_binaryFormat, 
+                            self.enableRendering, 
+                            self.enablePicking,  
+                            self.alphaFactor, 
+                            self.scope,  
+                            self.hasAlignment)
+                            
+        if self.hasAlignment:
+            data += pack(self.M3GNode_binaryFormat_2, 
+                         self.zTarget,  
+                         self.yTarget, 
+                         getId(self.zReference),  
+                         getId(self.yReference)) 
+        return data
+        
+    def getDataLength(self):
+        value = M3GTransformable.getDataLength(self) + \
+                     struct.calcsize(self.M3GNode_binaryFormat)
+        if self.hasAlignment:
+            value += struct.calcsize(self.M3GNode_binaryFormat_2)
+        return value
+        
+    def writeJava(self,aWriter,aCreate):
+        if aCreate: pass #Abstract Base Class! Cant't be created
+        M3GTransformable.writeJava(self,aWriter,False)    
+        
+class M3GGroup(M3GNode):
+    def __init__(self):
+        M3GNode.__init__(self)
+        self.ObjectType=9
+        self.children = [] #ObjectIndex[] 
+        
+    def searchDeep(self,alist):
+        for element in self.children:
+            alist = element.searchDeep(alist)
+        return M3GNode.searchDeep(self,alist)
+
+    def writeJava(self,aWriter,aCreate):
+        if aCreate:
+            aWriter.write(2,"//Group:"+self.name )
+            aWriter.write(2,"Group BL"+str(self.id)+" = new Group();")
+        M3GNode.writeJava(self,aWriter,False)
+        for element in self.children:
+            aWriter.write(2,"BL%i.addChild(BL%i);" % (self.id,element.id))
+   
+    def getData(self):
+        data = M3GNode.getData(self)
+        data = data + struct.pack("<I",len(self.children))
+        for element in self.children:
+            data += struct.pack("<I",getId(element))
+        return data
+    
+    def getDataLength(self):
+        return M3GNode.getDataLength(self)+ \
+                 struct.calcsize("<"+str(len(self.children)+1)+"I")
+
+        
+class M3GWorld(M3GGroup):
+    def __init__(self):
+        M3GGroup.__init__(self)
+        self.ObjectType=22
+        self.activeCamera=None #ObjectIndex 
+        self.background=None #ObjectIndex UInt32 0=None
+        self.M3GWorld_binaryFormat='<II'
+        
+    def searchDeep(self,alist):
+        alist = doSearchDeep([self.activeCamera, self.background],alist)
+        return M3GGroup.searchDeep(self,alist)
+
+        
+    def writeJava(self,aWriter,aCreate):
+        if aCreate:
+            aWriter.write(2,"//World:"+self.name )
+            aWriter.write(2,"World BL"+str(self.id)+" = new World();")
+        M3GGroup.writeJava(self,aWriter,False)
+        if self.background != None:
+            aWriter.write(2,"BL"+str(self.id)+".setBackground(BL"+str(self.background.id)+");")
+        if self.activeCamera != None:
+            aWriter.write(2,"BL%i.setActiveCamera(BL%i);" %
+                            (self.id,self.activeCamera.id))
+        aWriter.write(2)
+
+        
+    def getData(self):
+        data = M3GGroup.getData(self)
+        return data + \
+                struct.pack(self.M3GWorld_binaryFormat,getId(self.activeCamera),getId(self.background))
+
+
+    def getDataLength(self):
+        return M3GGroup.getDataLength(self) + struct.calcsize(self.M3GWorld_binaryFormat)
+            
+class M3GBackground(M3GObject3D):
+    def __init__(self):
+        M3GObject3D.__init__(self)
+        self.ObjectType=4
+        self.M3GBackground_binaryFormat = '<BBiiiiBB'
+        self.backgroundColor=M3GColorRGBA(0,0,0,0) #ColorRGBA 0x00000000 (black, transparent)
+        self.backgroundImage=None #ObjectIndex null (use the background color only)
+        self.backgroundImageModeX=32; #Byte BORDER=32 REPEAT=33
+        self.backgroundImageModeY=32; #Byte BORDER
+        self.cropX = 0; #Int32 
+        self.cropY = 0 #Int32 ;
+        self.cropWidth = 0 #Int32 ;
+        self.cropHeight = 0;#Int32 
+        self.depthClearEnabled = True #Boolean 
+        self.colorClearEnabled = True #Boolean 
+
+    def writeJava(self,aWriter,aCreate):
+        if aCreate:
+            aWriter.write(2,"//Background:"+self.name )
+            aWriter.write(2,"Background BL"+str(self.id)+" = new Background();")
+        M3GObject3D.writeJava(self,aWriter,False)
+        aWriter.write(2,"BL"+str(self.id)+".setColor("+self.backgroundColor.writeJava()+");")
+        aWriter.write(2,"")
+
+    def getData(self):
+        data = M3GObject3D.getData(self)
+        data += self.backgroundColor.getData()
+        data += struct.pack('<I',getId(self.backgroundImage))
+        data += struct.pack(self.M3GBackground_binaryFormat, self.backgroundImageModeX, 
+                            self.backgroundImageModeY,
+                            self.cropX, 
+                            self.cropY, 
+                            self.cropWidth, 
+                            self.cropHeight, 
+                            self.depthClearEnabled,  
+                            self.colorClearEnabled)
+        return data
+    
+    def getDataLength(self):
+        value=M3GObject3D.getDataLength(self)
+        value += self.backgroundColor.getDataLength()
+        value += struct.calcsize('<I')
+        value += struct.calcsize(self.M3GBackground_binaryFormat)
+        return value
+        
+        
+class M3GCamera(M3GNode):
+    GENERIC=48      #Projection-Types
+    PARALLEL=49
+    PERSPECTIVE=50
+    
+    def __init__(self):
+        M3GNode.__init__(self)
+        self.ObjectType=5
+        self.projectionType=M3GCamera.PARALLEL #Byte 
+        #IF projectionType==GENERIC, THEN
+        self.projectionMatrix=M3GMatrix() #Matrix \95view volume : opposite corners at (-1 -1 -1) and (1 1 1)
+        # TODO: Set right matrix    
+        #ELSE
+        self.fovy=0.0 #Float32 
+        self.AspectRatio=0.0#Float32 
+        self.near=0.0#Float32 
+        self.far=0.0#Float32 
+        #END
+    
+    def writeJava(self,aWriter,aCreate):
+        if aCreate:
+            aWriter.write(2,"//Camera " + self.name)
+            aWriter.write(2,"Camera BL%i = new Camera();" % (self.id))
+        M3GNode.writeJava(self,aWriter,False)
+        aWriter.write(2,"BL%i.setPerspective(%ff,  //Field of View" % \
+                          (self.id,self.fovy))
+        aWriter.write(4,"(float)aCanvas.getWidth()/(float)aCanvas.getHeight(),")
+        aWriter.write(4,str(self.near)+"f, //Near Clipping Plane")
+        aWriter.write(4,str(self.far)+"f); //Far Clipping Plane")              
+        
+    def getData(self):
+        data = M3GNode.getData(self)
+        data += struct.pack("B",self.projectionType)
+        if self.projectionType == self.GENERIC:
+            data += self.projectionMatrix.getData()
+        else:
+            data += struct.pack("<4f",self.fovy,self.AspectRatio,self.near,self.far)
+        return data
+    
+    def getDataLength(self):
+        value = M3GNode.getDataLength(self)
+        value += struct.calcsize("B")
+        if self.projectionType == self.GENERIC:
+            value += self.projectionMatrix.getDataLength()
+        else:
+            value += struct.calcsize("<4f")
+        return value
+        
+
+class M3GMesh(M3GNode):
+    def __init__(self,aVertexBuffer=None, aIndexBuffer=[], aAppearance=[]):
+        M3GNode.__init__(self)
+        self.ObjectType=14
+        self.vertexBuffer = aVertexBuffer #ObjectIndex 
+        self.submeshCount=len(aIndexBuffer) #UInt32 
+        #FOR each submesh...
+        self.indexBuffer=aIndexBuffer #ObjectIndex 
+        self.appearance=aAppearance #;ObjectIndex 
+        #END
+
+    def getData(self):
+        data = M3GNode.getData(self)
+        data += struct.pack('<2I', getId(self.vertexBuffer), 
+                                  self.submeshCount)
+        for i in range(len(self.indexBuffer)):
+            data += struct.pack('<2I',getId(self.indexBuffer[i]),
+                                      getId(self.appearance[i]))
+        return data
+        
+    def getDataLength(self):
+        value = M3GNode.getDataLength(self)
+        value += struct.calcsize('<2I')
+        for i in range(len(self.indexBuffer)):
+            value += struct.calcsize('<2I')
+        return value
+            
+    def searchDeep(self,alist):
+        alist = doSearchDeep([self.vertexBuffer] +self.indexBuffer
+                              + self.appearance ,alist)
+        return M3GNode.searchDeep(self,alist)
+            
+    def writeJava(self,aWriter,aCreate):
+        self.writeBaseJava(aWriter,aCreate,"Mesh","")
+        
+    def writeBaseJava(self,aWriter,aCreate,aClassName,aExtension):
+        if aCreate:
+            aWriter.writeClass(aClassName,self)
+            if self.submeshCount > 1:
+                aWriter.write(2,"IndexBuffer[] BL%i_indexArray = {" % (self.id))
+                aWriter.write(4,",".join(["BL%i" %(i.id) for i in self.indexBuffer ]))
+                aWriter.write(2,"                                };")
+                aWriter.write(2)
+                aWriter.write(2,"Appearance[] BL%i_appearanceArray = {" % (self.id))
+                aWriter.write(4,",".join(["BL%i" %(i.id) for i in self.appearance ]))
+                aWriter.write(2,"                                };")
+                aWriter.write(2)
+                aWriter.write(2,"%s BL%i = new %s(BL%i,BL%i_indexArray,BL%i_appearanceArray%s);" % \
+                                (aClassName,self.id,aClassName,self.vertexBuffer.id, self.id,self.id,aExtension))
+            else:
+                #print "indexBuffer", len(self.indexBuffer)
+                #print "appearance", len(self.appearance)
+                aWriter.write(2,"%s BL%i = new %s(BL%i,BL%i,BL%i%s);" % \
+                                (aClassName, 
+                                 self.id,
+                                 aClassName,
+                                 self.vertexBuffer.id,
+                                 self.indexBuffer[0].id,
+                                 self.appearance[0].id,
+                                 aExtension))
+        M3GNode.writeJava(self,aWriter,False)
+        aWriter.write(2)  
+
+
+class M3GSkinnedMesh(M3GMesh):
+    def __init__(self,aVertexBuffer=None, aIndexBuffer=[], aAppearance=[]):
+        M3GMesh.__init__(self,aVertexBuffer, aIndexBuffer, aAppearance)
+        self.ObjectType=16
+        self.skeleton=None #ObjectIndex
+        self.bones={}
+        #print"M3GSkinnedMesh.__init__::self.vertexBuffer:",self.vertexBuffer
+        ##ObjectIndex skeleton;
+        ##UInt32 transformReferenceCount;
+        ##FOR each bone reference... 
+        ##    ObjectIndex transformNode; 
+        ##    UInt32 firstVertex; 
+        ##    UInt32 vertexCount; 
+        ##    Int32 weight;
+        ##END
+
+    def searchDeep(self,alist):
+        alist = doSearchDeep([self.skeleton],alist)
+        return M3GMesh.searchDeep(self,alist)
+
+    def addSecondBone(self):
+        secondBones = {}
+        for bone in self.bones.values():
+            bone2 = M3GBone()
+            bone2.verts=bone.verts
+            bone.verts=[]
+            mGroup = M3GGroup()
+            mGroup.name=bone.transformNode.name+"_second"
+            bone2.transformNode=mGroup
+            bone2.references = bone.references
+            bone.references = [] 
+            bone2.weight = bone.weight
+            bone.weight=0
+            mGroup.children = bone.transformNode.children
+            bone.transformNode.children = [mGroup]
+            mGroup.animationTracks=bone.transformNode.animationTracks
+            bone.transformNode.animationTracks = []
+            secondBones[bone.transformNode.name+"_second"]=bone2
+        for bone in secondBones.values():
+            self.bones[bone.transformNode.name] = bone
+            
+    def getBlenderIndexes(self):
+        #print "M3GSkinnedMesh.vertexBuffer:",self.vertexBuffer
+        return self.vertexBuffer.positions.blenderIndexes
+    
+    def writeJava(self,aWriter,aCreate):
+        self.writeBaseJava(aWriter,aCreate,"SkinnedMesh",
+                           (",BL%i" % (self.skeleton.id)))
+        aWriter.write(2,"//Transforms")
+        for bone in self.bones.values():
+            #print "bone: ", bone
+            #print "bone.references: ", bone.references
+            for ref in bone.references:
+                aWriter.write(2,"BL%i.addTransform(BL%i,%i,%i,%i);" % 
+                                 (self.id,
+                                  bone.transformNode.id,bone.weight,
+                                  ref.firstVertex, ref.vertexCount))
+        aWriter.write(2)
+        
+    def getDataLength(self):
+        value = M3GMesh.getDataLength(self)
+        value += struct.calcsize('<I')  #skeleton
+        value += struct.calcsize('<I')  #transformReferenceCount
+        for bone in self.bones.values():
+            for ref in bone.references:
+                value += struct.calcsize('<3Ii')
+        return value
+    def getData(self):
+        data = M3GMesh.getData(self)
+        data += struct.pack('<I', getId(self.skeleton))
+        count = 0
+        for bone in self.bones.values(): count+=len(bone.references)
+        data += struct.pack('<I',count)
+        for bone in self.bones.values():
+            for ref in bone.references:
+                data += struct.pack('<I',getId(bone.transformNode))
+                data += struct.pack('<2I',ref.firstVertex,ref.vertexCount)
+                data += struct.pack('<i',bone.weight)
+        return data
+
+class M3GLight(M3GNode):
+    def __init__(self):
+        M3GNode.__init__(self)
+        self.ObjectType=12
+        self.modes = {'AMBIENT':128,
+                      'DIRECTIONAL':129,
+                      'OMNI':130,
+                      'SPOT':131}
+        self.attenuationConstant = 1.0 #Float32
+        self.attenuationLinear = 0.0 #Float32 
+        self.attenuationQuadratic = 0.0 #Float32 
+        self.color = M3GColorRGB(1.0, 1.0, 1.0) #ColorRGB 
+        self.mode = self.modes['DIRECTIONAL'] #Byte Enumurator mode: DIRECTIONAL
+        self.intensity = 1.0 #Float32 
+        self.spotAngle = 45 #Float32 
+        self.spotExponent = 0.0 #Float32
+    
+    def writeJava(self,aWriter,aCreate):
+        if aCreate:
+            aWriter.write(2,"//Light: " + self.name)
+            aWriter.write(2,"Light BL%i = new Light();" % (self.id))
+        aWriter.write(2,"BL%i.setMode(%i);" % (self.id,self.mode)) #Light.OMNI
+        if self.mode in [self.modes['OMNI'],self.modes['SPOT']]:#Attenuation
+            aWriter.write(2,"BL%i.setAttenuation(%ff, %ff,%ff);" 
+                             % (self.id,
+                                self.attenuationConstant,
+                                self.attenuationLinear, 
+                                self.attenuationQuadratic))
+        aWriter.write(2,("BL%i.setColor("+self.color.writeJava()+");") 
+                             % (self.id))
+        aWriter.write(2,"BL%i.setIntensity(%ff);" 
+                             % (self.id,self.intensity))
+        if self.mode == self.modes['SPOT']:
+            aWriter.write(2,"BL%i.setSpotAngle(%ff);" 
+                             % (self.id,self.spotAngle))
+            aWriter.write(2,"BL%i.setSpotExponent(%ff);" 
+                             % (self.id,self.spotExponent))
+        M3GNode.writeJava(self,aWriter,False)
+        aWriter.write(2)
+        
+        
+    def getData(self):
+        data = M3GNode.getData(self)
+        data += struct.pack("<fff", self.attenuationConstant,
+                                    self.attenuationLinear, 
+                                    self.attenuationQuadratic) 
+        data += self.color.getData() 
+        data += struct.pack("<Bfff", self.mode,
+                                     self.intensity, 
+                                     self.spotAngle, 
+                                     self.spotExponent)
+        return data
+
+    def getDataLength(self):
+        value = M3GNode.getDataLength(self)
+        value += self.color.getDataLength()
+        value += struct.calcsize('<B6f')
+        return value
+        
+class  M3GMaterial(M3GObject3D):
+    def __init__(self):
+        M3GObject3D.__init__(self)
+        self.ObjectType=13
+        self.ambientColor=M3GColorRGB(0.2, 0.2, 0.2) #ColorRGB 
+        self.diffuseColor=M3GColorRGBA(0.8, 0.8, 0.8, 1.0) #ColorRGBA 
+        self.emissiveColor=M3GColorRGB(0.0, 0.0, 0.0) #ColorRGB 
+        self.specularColor=M3GColorRGB(0.0, 0.0, 0.0) #ColorRGB 
+        self.shininess=0.0 #Float32 
+        self.vertexColorTrackingEnabled=False #Boolean
+
+    def writeJava(self,aWriter,aCreate):
+        if aCreate :
+            aWriter.write(2,"//Material: "+self.name )
+            aWriter.write(2,"Material BL%i = new Material();" % (self.id))
+        aWriter.write(2,("BL%i.setColor(Material.AMBIENT," + 
+                                 self.ambientColor.writeJava()+");") % (self.id) )
+        aWriter.write(2,("BL%i.setColor(Material.SPECULAR," + 
+                                 self.specularColor.writeJava()+");") % (self.id) )
+        aWriter.write(2,("BL%i.setColor(Material.DIFFUSE," +
+                                 self.diffuseColor.writeJava()+");") % (self.id))
+        aWriter.write(2,("BL%i.setColor(Material.EMISSIVE," +
+                                 self.emissiveColor.writeJava()+");") % (self.id))
+        aWriter.write(2,("BL%i.setShininess(%ff);") % (self.id,self.shininess))
+        aWriter.write(2,("BL%i.setVertexColorTrackingEnable(" + 
+                               toJavaBoolean(self.vertexColorTrackingEnabled) + ");") % 
+                                (self.id))
+        M3GObject3D.writeJava(self,aWriter,False)
+        
+    def getData(self):
+        data = M3GObject3D.getData(self)
+        data += self.ambientColor.getData()
+        data += self.diffuseColor.getData()
+        data += self.emissiveColor.getData()
+        data += self.specularColor.getData()
+        data += struct.pack('<fB',self.shininess,
+                                  self.vertexColorTrackingEnabled)
+        return data
+
+
+    def getDataLength(self):
+        value = M3GObject3D.getDataLength(self)
+        value += self.ambientColor.getDataLength()
+        value += self.diffuseColor.getDataLength()
+        value += self.emissiveColor.getDataLength()
+        value += self.specularColor.getDataLength()
+        value += struct.calcsize('<fB')
+        return value
+
+
+class M3GVertexArray(M3GObject3D):
+    def __init__(self,aNumComponents,aComponentSize,aAutoScaling=False,aUVMapping=False):
+        M3GObject3D.__init__(self)
+        self.ObjectType=20
+        self.blenderIndexes={} #Translation-Table from Blender index to m3g index
+        self.autoscaling = aAutoScaling #bias and scale should be computed internal
+        self.uvmapping=aUVMapping #Change coordinates from blender uv to uv-m3g
+        self.bias = [0.0,0.0,0.0] 
+        self.scale = 1.0 
+        self.componentSize=aComponentSize #Byte number of bytes per component; must be [1, 2]
+        self.componentCount=aNumComponents #Byte number of components per vertex; must be [2, 4]
+        self.encoding=0 #Byte 0="raw" as bytes or 16 bit integers.
+        self.vertexCount=0 #UInt16 number of vertices in this VertexArray; must be [1, 65535]
+        if self.autoscaling==True:
+            self.components = array('f')
+        else:
+            self.components = self.createComponentArray()
+        #FOR each vertex...
+        # IF componentSize==1, THEN
+        #  IF encoding==0, THEN
+        #   Byte[componentCount] 
+        #  ELSE IF encoding==1, THEN
+        #   Byte[componentCount] 
+        #  END
+        # ELSE
+        #  IF encoding==0, THEN
+        #   Int16[componentCount] 
+        #  ELSE IF encoding==1, THEN
+        #   Int16[componentCount] 
+        #  END
+        # END
+        #END
+    
+    def createComponentArray(self):
+        if self.componentSize == 1:
+            return array('b') #Byte-Array
+        else:
+            return array('h') #Short-Array
+            
+    def useMaxPrecision(self,aBoundingBox):
+        """With Bias and Scale you can maximize the precision of the array"""
+        #print "useMaxPrecision"
+        vertexList = M3GVertexList(aBoundingBox)
+        first = vertexList[0]
+        minimum =[first[0],first[1],first[2]]
+        maximum = [first[0],first[1],first[2]] #Search maximal Dimension
+        for element in vertexList:
+            for i in range(3):
+                if minimum[i] > element[i] : minimum[i] = element[i]
+                if maximum[i] < element[i] : maximum[i] = element[i]
+                #print i, maximum[i],element[i]
+        lrange=[0,0,0]
+        maxRange=0.0
+        maxDimension=-1
+        for i in range(3):  #set bias
+            lrange[i] = maximum[i]-minimum[i]
+            self.bias[i] = minimum[i]*0.5+maximum[i]*0.5
+            #print "Bias",i,self.bias[i],"min-max",minimum[i],maximum[i],"lrang",lrange[i]
+            if lrange[i] > maxRange:
+                maxRange = lrange[i]
+                maxDimension=i
+        self.scale = maxRange/65533.0
+        #print "MaxRange ",maxRange
+        #print "scale",self.scale
+
+
+    def internalAutoScaling(self):
+        print "internalAutoScaling"
+        #Already done?
+        print self.components.typecode
+        if not self.autoscaling or self.components.typecode!="f":return
+        #Find bais and scale
+        minimum=[]
+        maximum=[]
+        for i in range(self.componentCount):
+            minimum.append(self.components[i])
+            maximum.append(self.components[i])         
+        for i in range(0,len(self.components),self.componentCount):
+            for j in range(self.componentCount):
+                if minimum[j] > self.components[i+j] : minimum[j] = self.components[i+j]
+                if maximum[j] < self.components[i+j] : maximum[j] = self.components[i+j]
+                #print "i+j=",i+j,"min=",minimum[j],"max=",maximum[j],"elem=",self.components[i+j]
+        #print "min=", minimum
+        #print "max=", maximum
+        lrange=[0] * self.componentCount
+        maxRange=0.0
+        maxDimension=-1
+        for i in range(self.componentCount):  #set bias
+            lrange[i] = maximum[i]-minimum[i]
+            self.bias[i] = minimum[i]*0.5+maximum[i]*0.5
+            #print "Bias",i,self.bias[i],"min-max",minimum[i],maximum[i],"lrang",lrange[i]
+            if lrange[i] > maxRange:
+                maxRange = lrange[i]
+                maxDimension=i
+        maxValue=(2**(8*self.componentSize)*1.0)-2.0
+        #print "MaxValue=",maxValue
+        self.scale = maxRange/maxValue
+        #print "MaxRange ",maxRange
+        #print "scale",self.scale
+        #Copy Components
+        oldArray=self.components
+        self.components=self.createComponentArray()
+        for i in range(0,len(oldArray),self.componentCount):
+            for j in range(self.componentCount):
+                element=int((oldArray[i+j]-self.bias[j])/self.scale)
+                #print "element",element
+                self.components.append(element)
+        # Reverse t coordinate because M3G uses a different 2D coordinate system than Blender.
+        if self.uvmapping:
+            for i in range(0,len(self.components),2):
+                self.components[i]= int(self.components[i]*(-1))
+        for i in range(len(self.components)):
+            if abs(self.components[i])>maxValue:raise Exception( i+". element too great/small!")
+                
+    def writeJava(self,aWriter,aCreate):
+        self.internalAutoScaling()
+        if aCreate:
+            aWriter.write(2,"// VertexArray " + self.name)
+            if self.componentSize == 1:
+                aWriter.write(2,"byte[] BL%i_array = {" % (self.id))
+            else:
+                aWriter.write(2,"short[] BL%i_array = {" % (self.id))
+            aWriter.writeList(self.components)
+            aWriter.write(2,"};")
+            aWriter.write(2)
+            aWriter.write(2,"VertexArray BL%i = new VertexArray(BL%i_array.length/%i,%i,%i);" %
+                        (self.id,self.id,
+                            self.componentCount,self.componentCount,self.componentSize))
+            aWriter.write(2,"BL%i.set(0,BL%i_array.length/%i,BL%i_array);" % 
+                    (self.id,self.id,self.componentCount,self.id))
+        M3GObject3D.writeJava(self,aWriter,False)
+        aWriter.write(2)
+     
+    
+    def getData(self):
+        self.internalAutoScaling()
+        self.vertexCount = len(self.components)/self.componentCount
+        data = M3GObject3D.getData(self)
+        data += struct.pack('<3BH',self.componentSize,
+                                 self.componentCount,
+                                 self.encoding,
+                                 self.vertexCount)
+        componentType = ""
+        if self.componentSize == 1:
+            componentType = "b"
+        else:
+            componentType = "h"
+        for element in self.components:
+            data += struct.pack('<'+componentType,element)
+        return data
+        
+    def getDataLength(self):
+        self.internalAutoScaling()
+        value = M3GObject3D.getDataLength(self)
+        value += struct.calcsize('<3BH')
+        componentType = ""
+        if self.componentSize == 1:
+            componentType = "b"
+        else:
+            componentType = "h"
+        value += struct.calcsize('<'+str(len(self.components))+componentType)
+        return value
+        
+    def append(self,element,index=None):
+        #print "type(element):",type(element)
+        if type(element) is Types.vectorType :
+            for i in range(3):
+                value = int((element[i]-self.bias[i])/self.scale)  
+                #print "append:",i,element[i],(element[i]-self.bias[i]),value                  
+                self.components.append(value)
+        elif type(element) is Types.MVertType:
+            for i in range(3):
+                value = int((element.co[i]-self.bias[i])/self.scale)  
+                #print "append:",i,element[i],(element[i]-self.bias[i]),value                  
+                self.components.append(value)
+            if index!=None:
+                key=str(len(self.blenderIndexes))
+                #print"key,index:",key,index
+                self.blenderIndexes[key]=index
+                #print"blenderIndexes",self.blenderIndexes
+        else:
+            # print "VertexArray.append: element=",element
+            self.components.append(element)
+            
+class M3GVertexBuffer(M3GObject3D):
+    def __init__(self):
+        M3GObject3D.__init__(self)
+        self.ObjectType=21
+        self.defaultColor=M3GColorRGBA(255,255,255) #ColorRGBA 0xFFFFFFFF (opaque white).
+        self.positions = None #ObjectIndex 
+        self.positionBias = [0.0,0.0,0.0] #Float32[3] 
+        self.positionScale = 1.0 #Float32 
+        self.normals = None #ObjectIndex 
+        self.colors = None #ObjectIndex
+        self.texCoordArrays = [] 
+        self.texcoordArrayCount = 0 #UInt32 
+##        #FOR each texture coordinate array...
+##        self.texCoords = [] #ObjectIndex 
+##        self.texCoordBias=[] #Float32[3] 
+##        self.texCoordScale=[] #;Float32 
+##        #END
+##        #If a texture coordinate array has only two components, the corresponding texCoordBias[2] element
+##        #must be 0.0.
+##        #Null texture coordinate arrays are never serialized, regardless of their position. A single texture
+##        #coordinate array will therefore always be serialized as belonging to texturing unit 0, regardless of
+##        #its original unit it was assigned to.
+##        #There are as many references in the texture coordinates array as there are active texture units for
+##        #this geometry. The texture coordinate references are loaded sequentially from texture unit 0. If the
+##        #implementation supports more texture units than are specified, these are left in their default, inactive
+##        #state, with a null texture coordinate reference and an undefined bias and scale.
+##        #If more texture coordinate references are specified than are supported by the implementation, then
+##        #this must be treated as an error, as it would be in the API. The application can then decide on an
+##        #appropriate course of action to handle this case.
+
+    def searchDeep(self,alist):
+        if self.positions!=None: alist = self.positions.searchDeep(alist)
+        if self.normals != None: alist = self.normals.searchDeep(alist)
+        if self.colors!= None: alist = self.colors.searchDeep(alist)
+        alist = doSearchDeep(self.texCoordArrays, alist)
+        return M3GObject3D.searchDeep(self,alist)
+    
+    def setPositions(self,aVertexArray):
+        self.positions = aVertexArray
+        self.positionBias = aVertexArray.bias
+        self.positionScale = aVertexArray.scale
+    
+    def writeJava(self,aWriter,aCreate):
+        if aCreate:
+            aWriter.write(2,"//VertexBuffer"+self.name )
+            aWriter.write(2,"VertexBuffer BL%i = new VertexBuffer();" % (self.id))
+        aWriter.write(2,"float BL%i_Bias[] = { %ff, %ff, %ff};" %
+                            (self.id,self.positionBias[0],
+                             self.positionBias[1],self.positionBias[2]))
+        aWriter.write(2,"BL%i.setPositions(BL%i,%ff,BL%i_Bias);" % 
+                               (self.id, self.positions.id,
+                                self.positionScale,self.id))
+        aWriter.write(2,"BL%i.setNormals(BL%i);" % (self.id,self.normals.id))
+        #if self.colors != None: aWriter.write(2,"BL%i.setTexCoords(0,BL%i,1.0f,null);" % 
+        #                                           (self.id,self.colors.id))
+        lIndex = 0
+        for iTexCoord in self.texCoordArrays:
+            aWriter.write(2,"float BL%i_%i_TexBias[] = { %ff, %ff, %ff};" %
+                            (self.id,lIndex, iTexCoord.bias[0],
+                             iTexCoord.bias[1],iTexCoord.bias[2]))
+           #int index, javax.microedition.m3g.VertexArray194 texCoords, float scale, float[] bias
+            aWriter.write(2,"BL%i.setTexCoords(%i,BL%i,%ff,BL%i_%i_TexBias);" % 
+                               (self.id, lIndex, iTexCoord.id, iTexCoord.scale,self.id,lIndex))
+            lIndex += 1
+   
+        M3GObject3D.writeJava(self,aWriter,False)
+    
+    
+    def getData(self):
+        self.texcoordArrayCount = len(self.texCoordArrays)
+        data = M3GObject3D.getData(self)
+        data += self.defaultColor.getData()
+        data += struct.pack('<I4f3I',getId(self.positions),
+                                     self.positionBias[0],
+                                     self.positionBias[1],
+                                     self.positionBias[2],
+                                     self.positionScale,
+                                     getId(self.normals),
+                                     getId(self.colors),
+                                     self.texcoordArrayCount)
+        for iTexCoord in self.texCoordArrays:
+            data += struct.pack('<I', getId(iTexCoord))
+            data += struct.pack('<ffff', iTexCoord.bias[0],
+                                    iTexCoord.bias[1],
+                                    iTexCoord.bias[2],
+                                    iTexCoord.scale)
+        return data
+
+    
+    def getDataLength(self):
+        value = M3GObject3D.getDataLength(self)
+        value += self.defaultColor.getDataLength()
+        value += struct.calcsize('<I4f3I')
+        value += struct.calcsize('<Iffff') * len(self.texCoordArrays)
+        return value
+
+
+class M3GPolygonMode(M3GObject3D):
+    CULL_BACK=160
+    CULL_NONE=162
+    SHADE_FLAT=164
+    SHADE_SMOOTH=165
+    WINDING_CCW=168
+    WINDING_CW=169
+    
+    def __init__(self):
+        M3GObject3D.__init__(self)
+        self.ObjectType=8
+        self.culling=M3GPolygonMode.CULL_BACK #Byte
+        self.shading=M3GPolygonMode.SHADE_SMOOTH #Byte
+        self.winding=M3GPolygonMode.WINDING_CCW #Byte
+        self.twoSidedLightingEnabled = False #Boolean 
+        self.localCameraLightingEnabled = False #Boolean 
+        self.perspectiveCorrectionEnabled = False #Boolean
+        
+    def writeJava(self,aWriter,aCreate):
+        if aCreate:
+            aWriter.write(2,"PolygonMode BL%i = new PolygonMode();" % (self.id))
+        aWriter.write(2,"BL%i.setCulling(%i);" % (self.id,self.culling))
+        aWriter.write(2,"BL%i.setShading(%i);" % (self.id,self.shading))
+        aWriter.write(2,"BL%i.setWinding(%i);" % (self.id,self.winding))
+        aWriter.write(2,("BL%i.setTwoSidedLightingEnable(" + 
+                toJavaBoolean(self.twoSidedLightingEnabled) + ");") % 
+                       (self.id))
+        aWriter.write(2,("BL%i.setLocalCameraLightingEnable(" + 
+                toJavaBoolean(self.localCameraLightingEnabled) + ");") % 
+                       (self.id))
+        aWriter.write(2,("BL%i.setPerspectiveCorrectionEnable(" + 
+                toJavaBoolean(self.perspectiveCorrectionEnabled) + ");") % 
+                       (self.id))
+        aWriter.write(2)
+        M3GObject3D.writeJava(self,aWriter,False)
+    
+    def getData(self):
+        data = M3GObject3D.getData(self)
+        data += struct.pack('6B',self.culling,
+                                 self.shading,
+                                 self.winding,
+                                 self.twoSidedLightingEnabled, 
+                                 self.localCameraLightingEnabled, 
+                                 self.perspectiveCorrectionEnabled)
+        return data
+
+    def getDataLength(self):
+        value = M3GObject3D.getDataLength(self)
+        value += struct.calcsize('6B')
+        return value
+
+class M3GIndexBuffer(M3GObject3D):
+    def __init__(self):
+        M3GObject3D.__init__(self)
+
+    def getData(self):
+        return M3GObject3D.getData(self)
+        
+    def getDataLength(self):
+        return M3GObject3D.getDataLength(self)
+    
+    def writeJava(self,aWriter,aCreate):
+        M3GObject3D.writeJava(self,aWriter,False)
+        
+        
+class M3GTriangleStripArray(M3GIndexBuffer):
+    def __init__(self):
+        M3GIndexBuffer.__init__(self)
+        self.ObjectType=11 
+        self.encoding=128 #Byte Bit 7: 1 = explicit property on index buffer true
+                      #Bit 1 .. 6: 0 = "raw" integer values 1= a single byte will suffice
+                                  #2 = a 16 bit integer is suffi to hold all the given index values
+        #IF encoding == 0, THEN
+        #self.startIndex = 0  #;UInt32 
+        #ELSE IF encoding == 1, THEN
+        #Byte startIndex;
+        #ELSE IF encoding == 2, THEN
+        #UInt16 startIndex;
+        #ELSE IF encoding == 128, THEN
+        self.indices = [] #;UInt32[]
+        #ELSE IF encoding == 129, THEN
+        #Byte[] indices;
+        #ELSE IF encoding == 130, THEN
+        #UInt16[] indices;
+        #END
+        self.stripLengths = [] #;UInt32[]
+
+    def writeJava(self,aWriter,aCreate):
+        if aCreate:
+            aWriter.write(2,"//length of TriangleStrips")
+            aWriter.write(2,"int[] BL"+str(self.id)+"_stripLength ={"+
+                ",".join([str(element) for element in self.stripLengths])+"};")
+            aWriter.write(2)
+            aWriter.write(2,"//IndexBuffer")
+            aWriter.write(2,"int[] BL%i_Indices = {" % (self.id))
+            aWriter.write(2,",".join([str(element) for element in self.indices])+"};")
+            aWriter.write(2)
+            aWriter.write(2,"IndexBuffer BL%i=new TriangleStripArray(BL%i_Indices,BL%i_stripLength);" %
+                            (self.id, self.id, self.id))
+        M3GIndexBuffer.writeJava(self,aWriter,False)
+        aWriter.write(2)
+     
+    
+    def getData(self):
+        data = M3GIndexBuffer.getData(self)
+        data += struct.pack('<BI',self.encoding,
+                                  len(self.indices))
+        for element in self.indices:
+            data += struct.pack('<I',element)
+        data += struct.pack('<I',len(self.stripLengths))
+        for element in self.stripLengths:
+            data += struct.pack('<I',element)
+        return data
+    
+    def getDataLength(self):
+        value = M3GIndexBuffer.getDataLength(self)
+        value += struct.calcsize('<BI')
+        if len(self.indices) > 0 :
+            value += struct.calcsize('<' + str(len(self.indices)) + 'I')
+        value += struct.calcsize('<I')
+        if len(self.stripLengths) > 0:
+            value+= struct.calcsize('<'+str(len(self.stripLengths))+'I')
+        return value
+        
+        
+class M3GAppearance(M3GObject3D):
+    def __init__(self):
+        M3GObject3D.__init__(self)
+        self.ObjectType=3
+        self.layer=0 #Byte 
+        self.compositingMode=None #ObjectIndex
+        self.fog=None #ObjectIndex 
+        self.polygonMode=None #ObjectIndex 
+        self.material=None #ObjectIndex 
+        self.textures=[] #;ObjectIndex[]
+        
+    def searchDeep(self,alist):
+        alist = doSearchDeep([self.compositingMode,self.fog,
+                              self.polygonMode,self.material]
+                             + self.textures,alist)
+        return M3GObject3D.searchDeep(self,alist)
+
+    def getData(self):
+        data = M3GObject3D.getData(self)
+        data += struct.pack("<B5I", self.layer,
+                                    getId(self.compositingMode),
+                                    getId(self.fog), 
+                                    getId(self.polygonMode), 
+                                    getId(self.material), 
+                                    len(self.textures))
+        for element in self.textures:
+            data += struct.pack("<I",getId(element))
+        return data
+    
+    def getDataLength(self):
+        value = M3GObject3D.getDataLength(self)
+        value += struct.calcsize("<B5I")
+        if len(self.textures) > 0 : 
+            value += struct.calcsize("<"+str(len(self.textures))+'I')
+        return value
+        
+        
+    def writeJava(self,aWriter,aCreate):
+        if aCreate:
+            aWriter.write(2,"//Appearance")
+            aWriter.write(2,"Appearance BL%i = new Appearance();" % (self.id))
+        if self.compositingMode!= None:
+            aWriter.write(2,"BL%i.setPolygonMode(BL%i);" % 
+                             (self.id,self.compositingMode.id))
+        if self.fog!=None:
+            aWriter.write(2,"BL%i.setFog(BL%i);" %
+                              (self.id,self.fog.id))
+        if self.polygonMode!=None:
+            aWriter.write(2,"BL%i.setPolygonMode(BL%i);" %
+                              (self.id,self.polygonMode.id))
+        if self.material!=None: 
+            aWriter.write(2,"BL%i.setMaterial(BL%i);" %
+                              (self.id,self.material.id))
+        i=0
+        for itexture in self.textures:
+            aWriter.write(2,"BL%i.setTexture(%i,BL%i);" %           
+                             (self.id,i,itexture.id))
+            i =+ 1
+        M3GObject3D.writeJava(self,aWriter,False)
+        aWriter.write(2)  
+
+class M3GTexture2D(M3GTransformable):
+    #M3G imposes the following restrictions when assigning textures to a model: 
+    #The dimensions must be powers of two (4, 8, 16, 32, 64, 128...).
+
+    WRAP_REPEAT = 241
+    WRAP_CLAMP = 240
+    FILTER_BASE_LEVEL=208
+    FILTER_LINEAR=209
+    FILTER_NEAREST=210
+    FUNC_ADD=224
+    FUNC_BLEND=225
+    FUNC_DECAL=226
+    FUNC_MODULATE=227
+    FUNC_REPLACE=228
+
+    def __init__(self,aImage):
+        M3GTransformable.__init__(self)
+        self.ObjectType=17
+        self.Image = aImage #ObjectIndex
+        self.blendColor=M3GColorRGB(0,0,0)
+        self.blending=M3GTexture2D.FUNC_MODULATE #Byte
+        self.wrappingS=M3GTexture2D.WRAP_REPEAT #Byte 
+        self.wrappingT=M3GTexture2D.WRAP_REPEAT #Byte 
+        self.levelFilter=M3GTexture2D.FILTER_BASE_LEVEL #Byte 
+        self.imageFilter=M3GTexture2D.FILTER_NEAREST #Byte
+
+    def searchDeep(self,alist):
+        alist = doSearchDeep([self.Image],alist)
+        return M3GTransformable.searchDeep(self,alist)
+
+    def getData(self):
+        data = M3GTransformable.getData(self)
+        data += struct.pack('<I', getId(self.Image))
+        data += self.blendColor.getData()
+        data += struct.pack('5B',self.blending,
+                                 self.wrappingS, 
+                                 self.wrappingT, 
+                                 self.levelFilter, 
+                                 self.imageFilter)
+        return data
+    
+    def getDataLength(self):
+        value = M3GTransformable.getDataLength(self)
+        value += struct.calcsize('<I')
+        value += self.blendColor.getDataLength()
+        value += struct.calcsize('5B')
+        return value
+            
+    def writeJava(self,aWriter,aCreate):
+        if aCreate:
+            aWriter.write(2,"//Texture")
+            aWriter.write(2,"Texture2D BL%i = new Texture2D(BL%i);" % (self.id,
+                                                                      self.Image.id))
+        aWriter.write(2,"BL%i.setFiltering(%i,%i);" % (self.id,
+                                   self.levelFilter, 
+                                   self.imageFilter))
+        aWriter.write(2,"BL%i.setWrapping(%i,%i);" % (self.id,
+                                    self.wrappingS,
+                                    self.wrappingT))
+        aWriter.write(2,"BL%i.setBlending(%i);" % (self.id,self.blending))
+        aWriter.write(2)
+        M3GTransformable.writeJava(self,aWriter,False)
+
+class ImageFactory:
+    images={}
+    def getImage(self, image, externalReference):
+        # It's important to use getFilename() because getName() returns a 
+        # truncated string depending on the length of the file name.
+        filename = Blender.sys.expandpath(image.getFilename())
+
+        if self.images.has_key(filename):
+            image = self.images[filename]
+        elif externalReference:
+            # Check for file ending (only relevant for external images). The M3G specification 
+            # mandates only PNG support, but some devices might also support additional image types.
+            [path,ext] = splitext(filename)
+            if ext != ".png":
+                print "Warning: image file ends with " + ext + ". M3G specification only mandates PNG support."
+
+            image = M3GExternalReference()
+            image.URI = Blender.sys.basename(filename)
+            self.images[filename] = image
+        else:
+            image = M3GImage2D(image)
+            self.images[filename] = image
+        return image
+            
+    getImage = classmethod(getImage)
+
+        
+class M3GImage2D(M3GObject3D):
+    ALPHA=96             #a single byte per pixel, representing pixel opacity
+    LUMINANCE=97         #a single byte per pixel, representing pixel luminance.
+    LUMINANCE_ALPHA=98   #two bytes per pixel. The first: luminance, the second: alpha.
+    RGB=99               #three bytes per pixel, representing red, green and blue
+    RGBA=100             #four bytes per pixel, representing red, green, blue and alpha
+
+    def __init__(self, aImage, aFormat=RGBA):
+        M3GObject3D.__init__(self)
+        self.ObjectType=10
+        self.image=aImage #Blender Image
+        self.format=aFormat #Byte 
+        self.isMutable=False #Boolean changable or not
+        [self.width, self.height] = aImage.getSize()
+
+        #IF isMutable==false, THEN
+        self.palette=0 #Byte[] 
+        self.pixels = array('B') #Byte[] 
+        self.extractPixelsFromImage()
+        #END
+#For a palettised format, the pixels array contains a single palette 
+#index per pixel, and the palette array will contain up to 256 entries, 
+#each consisting of a pixel specifier appropriate to the format chosen.
+
+#For a non-palettised format, the palette array will be empty, 
+#and the pixels array contains a pixel specifier appropriate to the format 
+#chosen.
+#In a pixel specifier, each byte is scaled such that 0 represents the 
+#value 0.0 and 255 represents the value 1.0. The different formats 
+#require different data to be serialized, as follows:
+
+#The width and height of the image must be non-negative powers of two, but they need not be equal.
+
+    def getData(self):
+        data = M3GObject3D.getData(self)
+        data += struct.pack('2B', self.format, self.isMutable)
+        data += struct.pack('<2I', self.width, self.height)
+        if self.isMutable == False:
+            # TODO: support palettised formats also
+            # export palette data
+            data += struct.pack('<I', 0)
+            
+            # export pixel data
+            if self.format == M3GImage2D.RGBA:
+                #print "len pixels",len(self.pixels)
+                data += struct.pack('<I', len(self.pixels))
+                for pixel in self.pixels:
+                    data += struct.pack('B', pixel)
+            #elif...
+        return data
+
+    def getDataLength(self):
+        value = M3GObject3D.getDataLength(self)
+        value += struct.calcsize('2B')
+        value += struct.calcsize('<2I')
+        if self.isMutable == False:
+            # TODO: support palettised formats also
+            value+= struct.calcsize('<I')
+            
+            # pixel data size
+            if self.format == M3GImage2D.RGBA:
+                value += struct.calcsize('<I')
+                value += struct.calcsize(str(len(self.pixels))+'B')
+        return value
+    
+    def writeJava(self,aWriter,aCreate):
+        if aCreate:
+            lFileName = self.image.filename
+            if not Blender.sys.exists(lFileName) :
+                lFileName = Blender.sys.join(dirname(Blender.Get('filename')),
+                                             basename(self.image.filename))
+            elif not Blender.sys.exists(lFileName):
+                raise FileError, 'Image file not found!'
+            lTargetFile = Blender.sys.join(Blender.sys.dirname(aWriter.filename),
+                                             Blender.sys.basename(self.image.filename))   
+            copy_file(lFileName,lTargetFile)
+            #shutil.copyfile(lFileName,lTargetFile)
+            aWriter.write(2,"//Image2D")
+            aWriter.write(2,"Image BL%i_Image = null;" % (self.id))
+            aWriter.write(2,"try {")
+            aWriter.write(3,"BL%i_Image = Image.createImage(\"/%s\");" %
+                                    (self.id,basename(self.image.filename)))
+            aWriter.write(2,"} catch (IOException e) {")
+            aWriter.write(3,"e.printStackTrace();")
+            aWriter.write(2,"}")
+            aWriter.write(2,"Image2D BL%i = new Image2D(Image2D.RGBA,BL%i_Image);" % 
+                                    (self.id,self.id))   
+        aWriter.write(2)
+        M3GObject3D.writeJava(self,aWriter,False)
+        aWriter.write(2)  
+        
+    def extractPixelsFromImage(self):
+        # Reverse t coordiante because M3G uses a different 2D coordinate system than OpenGL.
+        for y in range(self.height):
+            for x in range(self.width):
+                [r, g, b, a] = self.image.getPixelI(x, self.height-1-y)
+                self.pixels.append(r)
+                self.pixels.append(g)
+                self.pixels.append(b)
+                self.pixels.append(a)
+                
+class M3GAnimationController(M3GObject3D):
+    def __init__(self):
+        M3GObject3D.__init__(self)
+        self.ObjectType=1
+        self.speed = 1.0 #Float32
+        self.weight = 1.0 #Float32
+        self.activeIntervalStart = 0 #Int32 - (always active)
+        self.activeIntervalEnd = 0 #Int32 
+        self.referenceSequenceTime = 0.0 #Float32 
+        self.referenceWorldTime = 0 #Int32 
+
+    def writeJava(self,aWriter,aCreate):
+        if aCreate:
+            aWriter.writeClass("AnimationController",self)
+            aWriter.write(2,"AnimationController BL%i = new AnimationController();" %
+                                   (self.id))
+        aWriter.write(2,"BL%i.setActiveInterval(%i, %i);" %
+                            (self.id,self.activeIntervalStart,self.activeIntervalEnd))
+        #lightAnim.setPosition(0, 2000);(2) Applying the animation during rendering
+        M3GObject3D.writeJava(self,aWriter,False)
+            
+    def getData(self):
+        data = M3GObject3D.getData(self)
+        data += struct.pack("<ffiifi", self.speed,
+                                    self.weight,
+                                    self.activeIntervalStart,
+                                    self.activeIntervalEnd, 
+                                    self.referenceSequenceTime, 
+                                    self.referenceWorldTime)
+        return data
+        
+    def getDataLength(self):
+        value = M3GObject3D.getDataLength(self)
+        return value + struct.calcsize("<ffiifi")
+                
+class M3GAnimationTrack(M3GObject3D):
+    ALPHA=256
+    AMBIENT_COLOR=257
+    COLOR=258
+    CROP=259
+    DENSITY=260
+    DIFFUSE_COLOR=261
+    EMISSIVE_COLOR=262
+    FAR_DISTANCE=263
+    FIELD_OF_VIEW=264
+    INTENSITY=265
+    MORPH_WEIGHTS=266
+    NEAR_DISTANCE=267
+    ORIENTATION=268
+    PICKABILITY=269
+    SCALE=270
+    SHININESS=271
+    SPECULAR_COLOR=272
+    SPOT_ANGLE=273
+    SPOT_EXPONENT=274
+    TRANSLATION=275
+    VISIBILITY=276
+
+    def __init__(self,aSequence,aProperty):
+        M3GObject3D.__init__(self)
+        self.ObjectType = 2
+        self.keyframeSequence = aSequence #ObjectIndex 
+        self.animationController = None #ObjectIndex
+        self.propertyID = aProperty #UInt32 
+    
+    def getData(self):
+        data = M3GObject3D.getData(self)
+        data += struct.pack("<3I", getId(self.keyframeSequence),
+                                   getId(self.animationController),
+                                   self.propertyID)
+        return data
+        
+    def getDataLength(self):
+        value = M3GObject3D.getDataLength(self)
+        return value + struct.calcsize("<3I")
+            
+    def writeJava(self,aWriter,aCreate):
+        if aCreate:
+                aWriter.writeClass("AnimationTrack",self)
+                #print "self.id,self.keyframeSequence,self.propertyID",self.id,self.keyframeSequence,self.propertyID
+                aWriter.write(2,"AnimationTrack BL%i = new AnimationTrack(BL%i,%i);" %
+                          (self.id,self.keyframeSequence.id,self.propertyID))
+        aWriter.write(2,"BL%i.setController(BL%i);" %
+                          (self.id,self.animationController.id))
+        M3GObject3D.writeJava(self,aWriter,False)
+        
+    def searchDeep(self,alist):
+        alist = doSearchDeep([self.keyframeSequence, self.animationController],alist)
+        return M3GObject3D.searchDeep(self,alist)
+       
+class M3GKeyframeSequence(M3GObject3D):
+    CONSTANT=192
+    LINEAR=176
+    LOOP=193
+    SLERP=177
+    SPLINE=178
+    SQUAD=179
+    STEP=180
+        
+    def __init__(self,aNumKeyframes, aNumComponents,aBlenderInterpolation,
+                      aM3GInterpolation=None):
+        M3GObject3D.__init__(self)
+        self.ObjectType = 19
+        if aM3GInterpolation!=None:
+            self.interpolation = aM3GInterpolation
+        else:
+            if aBlenderInterpolation == "Constant":
+                self.interpolation = self.STEP #Byte 
+            elif aBlenderInterpolation == "Bezier":
+                self.interpolation = self.SPLINE #Byte 
+            elif aBlenderInterpolation == "Linear":
+                self.interpolation = self.LINEAR #Byte 
+            else:
+                pass # TODO : Throw Error
+        self.repeatMode =  self.CONSTANT #Byte CONSTANT or LOOP
+        self.encoding = 0 #Byte 0=raw 
+        # TODO: Other encodings
+        self.duration = 0 #UInt32 
+        self.validRangeFirst = 0 #UInt32 
+        self.validRangeLast = 0 #UInt32 
+        self.componentCount = aNumComponents #UInt32
+        self.keyframeCount = aNumKeyframes #UInt32  
+        #IF encoding == 0
+        #FOR each key frame...
+        self.time = [] #Int32
+        self.vectorValue = [] # Float32[componentCount]
+        #END
+        #ELSE IF encoding == 1
+        #Float32[componentCount] vectorBias;
+        #Float32[componentCount] vectorScale;
+        #FOR each key frame...
+        #Int32 time;
+        #Byte[componentCount] vectorValue;
+        #END
+        #ELSE IF encoding == 2
+        #Float32[componentCount] vectorBias;
+        #Float32[componentCount] vectorScale;
+        #FOR each key frame...
+        #Int32 time;
+        #UInt16[componentCount] vectorValue;
+        #END
+        #END
+        
+#All of the vectorValue arrays are the same size, so a separate count is stored outside the individual
+#keyframe's data rather than with each array. The encoding field indicates the encoding scheme to be used for the keyframe data. Only the
+#nominated values above are allowed. Other values must be treated as errors.
+
+#\95Encoding 0 indicates that the values are stored "raw" as floats.
+#\95Encodings 1 and 2 indicate that the values are quantized to 1 or 2 bytes. For each component,
+#a bias and scale are calculated from the sequence of values for that component. The bias is the
+#mimimum value, the scale is the maximum value minus the minimum value. The raw values
+#are then converted to a value 0..1 by subtracting the bias and dividing by the scale. These raw
+#values are then quantized into the range of a Byte or UInt16 by multiplying by 255 or 65535
+#respectively. The converse operation restores the original value from the quantized values.
+
+    def beforeExport(self):
+        #M3G can not work with negative zero
+        #print"beforeExport ID= ",self.id
+        for i in range(self.keyframeCount):
+            for j in range(self.componentCount):
+                x = struct.pack("<f",self.vectorValue[i][j])
+                y = struct.unpack("<f",x)
+                #print "beforeExport i,j ",i,j,self.vectorValue[i][j],y
+                if abs(self.vectorValue[i][j]) < 0.000001 :
+                    #print "Negative Zero found!",self.vectorValue[i][j]
+                    self.vectorValue[i][j]=0.0
+                    #print "zero ",self.vectorValue[i][j]
+    
+    def getData(self):
+        self.beforeExport()
+        data = M3GObject3D.getData(self)
+        data += struct.pack("<3B5I",self.interpolation, 
+                                    self.repeatMode,
+                                    self.encoding, 
+                                    self.duration, 
+                                    self.validRangeFirst, 
+                                    self.validRangeLast, 
+                                    self.componentCount,
+                                    self.keyframeCount) 
+        #FOR each key frame...
+        for i in range(self.keyframeCount):
+            data += struct.pack("<i",self.time[i]) #Int32
+            for j in range(self.componentCount):
+                data += struct.pack("<f",self.vectorValue[i][j]) # Float32[componentCount]
+        return data
+
+    def getDataLength(self):
+        value = M3GObject3D.getDataLength(self)
+        value += struct.calcsize("<3B5I")
+        value += struct.calcsize("<i") * self.keyframeCount
+        value += struct.calcsize("<f") * self.keyframeCount * self.componentCount
+        return value
+        
+    def setRepeatMode(self,aBlenderMode):
+        if aBlenderMode == "Constant" :
+            self.repeatMode = self.CONSTANT
+        elif aBlenderMode == "Cyclic":
+            self.repeatMode = self.LOOP
+        else:
+            print "In IPO: Mode " + aBlenderMode + " is not assisted!" 
+
+    def setKeyframe(self, aIndex, aTime, aVector):
+        self.time.append(aTime)
+        self.vectorValue.append(aVector)
+            
+    def writeJava(self,aWriter,aCreate):
+        self.beforeExport()
+        if aCreate:
+            aWriter.writeClass("KeyframeSequence",self)
+            aWriter.write(2,"KeyframeSequence BL%i = new KeyframeSequence(%i, %i, %i);" %
+                            (self.id,self.keyframeCount,self.componentCount,self.interpolation))
+            for i in range(len(self.time)):
+                lLine = "BL%i.setKeyframe(%i,%i, new float[] { %ff, %ff, %ff" % \
+                                        (self.id,i,self.time[i],self.vectorValue[i][0], \
+                                          self.vectorValue[i][1],self.vectorValue[i][2])
+                if self.componentCount == 4:
+                    lLine += ", %ff" % (self.vectorValue[i][3])
+                lLine += "});"
+                aWriter.write(2,lLine)
+                    # TODO : Works only with componentCount = 3
+        aWriter.write(2,"BL%i.setDuration(%i);" % (self.id, self.duration))
+        aWriter.write(2,"BL%i.setRepeatMode(%i);" % (self.id,self.repeatMode))
+        M3GObject3D.writeJava(self,aWriter,False)
+            
+# ---- Translator -------------------------------------------------------------- #
+
+class M3GTranslator:
+    "Trys to translate a blender scene into a mg3 World"
+   
+    def __init__(self):
+        self.world = None
+        self.scene = None
+        self.nodes = []
+    
+    def start(self):
+        print "Translate started ..."
+        
+        self.scene = Blender.Scene.GetCurrent()
+        self.world = self.translateWorld(self.scene)
+        
+        for obj in self.scene.objects:
+            if obj.getType()=='Camera': #  older Version: isinstance(obj.getData(),Types.CameraType)
+                self.translateCamera(obj)
+            elif obj.getType()=='Mesh':
+                self.translateMesh(obj)
+            elif obj.getType()=='Lamp' and mOptions.lightingEnabled:  # older Version: isinstance(obj.getData(),Types.LampType)
+                self.translateLamp(obj)
+            elif obj.getType()=='Empty':
+                self.translateEmpty(obj)
+            else:
+                print "Warning: could not translate" + str(obj) + ". Try to convert object to mesh using Alt-C"
+                
+        self.translateParenting()
+            
+        print "Translate finished."
+        return self.world
+        
+    def translateParenting(self):
+        for iNode in self.nodes:
+            if iNode.parentBlenderObj == None:
+                self.world.children.append(iNode)
+            else:
+                for jNode in self.nodes:
+                    if iNode.parentBlenderObj == jNode.blenderObj:
+                        #TODO : Every object can be parent
+                        jNode.children.append(iNode)
+                        #lMatrix = Matrix(iNode.blenderMatrixWorld) * Matrix(jNode.blenderMatrixWorld).invert()
+                        lMatrix = self.calculateChildMatrix(iNode.blenderMatrixWorld,jNode.blenderMatrixWorld)
+                        iNode.transform = self.translateMatrix(lMatrix)
+                        iNode.hasGeneralTransform=True
+                        break
+                    
+    def calculateChildMatrix(self,child,parent):
+        return Matrix(child) * Matrix(parent).invert()
+    
+    def translateArmature(self,obj,meshObj,aSkinnedMesh):
+        print "translate Armature ..."
+        #print "translateArmature::aSkinnedMesh.vertexBuffer:",aSkinnedMesh.vertexBuffer
+        armature = obj.getData()
+        
+        #Pose
+        #pose = obj.getPose()
+        #print "pose ",pose
+        #for bone in pose.bones.values():
+        #    print "bone local",bone.localMatrix
+        #    print "bone pose",bone.poseMatrix
+        
+        #Skeleton
+        mGroup = M3GGroup()
+        self.translateCore(obj,mGroup)
+        aSkinnedMesh.skeleton = mGroup
+        mGroup.transform = self.translateMatrix(
+                               self.calculateChildMatrix(obj.matrixWorld,
+                                                         meshObj.matrixWorld))
+        
+        #Bones
+        #print "armature:",armature.bones
+        for bone in armature.bones.values(): #Copy Bones
+            mBone = M3GBone()
+            mBone.transformNode = M3GGroup()
+            self.translateCore(bone, mBone.transformNode)
+            #mBone.transformNode.transform = self.translateMatrix(pose.bones[bone.name].poseMatrix)#Test!!!!
+            #print "node transform", mBone.transformNode.transform
+            #mBone.transformNode.transform=self.translateMatrix(self.calculateChildMatrix(bone.matrix['ARMATURESPACE'],meshObj.matrixWorld))
+            if bone.hasParent():
+                mBone.transformNode.transform = self.translateMatrix(
+                                                    self.calculateChildMatrix(bone.matrix['ARMATURESPACE'],
+                                                                              bone.parent.matrix['ARMATURESPACE']))
+            mBone.weight = bone.weight
+            aSkinnedMesh.bones[bone.name]=mBone
+            
+        rootBone = [] #Copy Child-Parent-Structure
+        for bone in armature.bones.values():
+            mBone = aSkinnedMesh.bones[bone.name]
+            if not bone.hasParent(): 
+                rootBone.append(mBone)
+            if bone.hasChildren():
+                for childBone in bone.children:
+                    mChildBone = aSkinnedMesh.bones[childBone.name]
+                    mBone.transformNode.children.append(mChildBone.transformNode)
+        for rbone in rootBone:
+            aSkinnedMesh.skeleton.children.append(rbone.transformNode)
+        
+        #VertexGroups - Skinning
+        if armature.vertexGroups:
+            for boneName in aSkinnedMesh.bones.keys():
+                aSkinnedMesh.bones[boneName].setVerts(self.translateVertsGroup(meshObj.getData(False,True).getVertsFromGroup(boneName),
+                                                                              aSkinnedMesh))
+        #Envelope - Skinning
+        if armature.envelopes:
+            pass #TODO
+        
+        #Action
+        self.translateAction(obj,aSkinnedMesh)
+        aSkinnedMesh.addSecondBone()    
+        
+        
+    def translateVertsGroup(self,group,aSkinnedMesh):
+        #print "group: ",group
+        #print "items: ",aSkinnedMesh.getBlenderIndexes().items()
+        ergebnis = [int(k) for k,v in aSkinnedMesh.getBlenderIndexes().items() if v in group]
+        #print "ergebnis: ",ergebnis
+        return ergebnis
+    
+    def translateAction(self,armatureObj,aSkinnedMesh):
+        action = armatureObj.getAction()
+        if action==None: return
+        
+        print "tranlating Action ..."
+        if mOptions.exportAllActions:
+            lArmatureID = self.translateUserID(armatureObj.getData().name)
+            print "armatureID ", lArmatureID, armatureObj
+            for a in Blender.Armature.NLA.GetActions().values():
+                (lArmatureActionID,lEndFrame,lActionID) = self.translateActionName(a.name)
+                #print "action", a
+                #print "lArmatureID", lArmatureActionID
+                #print "lEndFrame", lEndFrame
+                #print "lActionID", lActionID
+                if lArmatureActionID == lArmatureID:
+                    #print "Action found"
+                    mController = self.translateActionIPOs(a,aSkinnedMesh,lEndFrame)
+                    mController.userID = lActionID
+                    #print "mController.userID ",mController.userID
+                    
+        #print "getActionScripts() ", Blender.Armature.NLA.getActionStrips()  
+        else:
+            self.translateActionIPOs(action,aSkinnedMesh)
+            
+        
+    def translateActionIPOs(self,aAction,aSkinnedMesh,aEndFrame=0):
+        ipos = aAction.getAllChannelIpos()
+        mController=None
+        for boneName in aSkinnedMesh.bones.keys():
+            if ipos.has_key(boneName):
+                ipo = ipos[boneName]
+                if mController==None: mController = M3GAnimationController()
+                self.translateIpo(ipo,aSkinnedMesh.bones[boneName].transformNode,mController,aEndFrame)
+        return mController
+    
+    def translateActionName(self,name):
+        # <Action Name>#A<M3G ID of Armature>E<End Frame>#<ID of Action>
+        lError = "Armature name " + name + " is not ok. Perhaps you should set option 'ExportAllAction' to false."
+        #print "name ", name
+        lLetter = name.find("#")
+        if lLetter == -1 :raise Exception(lError)
+        if name[lLetter+1]!='A': raise Exception(lError)
+        lName = name[lLetter+2:]
+        #print "lName ", lName
+        lLetter = lName.find("E")
+        #print "lLetter ", lLetter
+        if lLetter == -1 :raise Exception(lError)
+        #print "lName[:]", lName[:0]
+        lArmatureID = int(lName[:lLetter])
+        lName = lName[lLetter+1:]
+        lLetter = lName.find("#")
+        if lLetter == -1:raise Exception(lError)
+        lEndFrame = int(lName[:lLetter])
+        lActionID = int(lName[lLetter+1:])
+        return (lArmatureID,lEndFrame,lActionID)
+
+    
+    def translateWorld(self,scene):
+        "creates world object"
+        world = M3GWorld()
+
+        #Background
+        world.background = M3GBackground()
+        blWorld= scene.world
+        #AllWorlds = Blender.World.Get()  # Set Color
+        #if len(AllWorlds)>=1:            # world object available
+        if blWorld != None:
+            world.background.backgroundColor=self.translateRGBA(blWorld.getHor(),0)  # horizon color of the first world
+            if mOptions.createAmbientLight & mOptions.lightingEnabled:
+                lLight = M3GLight()
+                lLight.mode = lLight.modes['AMBIENT']
+                lLight.color = self.translateRGB(AllWorlds[0].getAmb())
+                self.nodes.append(lLight)
+
+        #TODO: Set background picture from world
+            
+        return world
+    
+    def translateEmpty(self,obj):
+        print "translate empty ..."
+        mGroup = M3GGroup()
+        self.translateToNode(obj,mGroup)
+            
+    def translateCamera(self,obj):
+        print "translate camera ..."
+        camera = obj.getData()
+        if camera.getType()!=0:
+            print "Only perscpectiv cameras will work korrekt"
+            return #Type=0 'perspectiv' Camera will be translated
+        mCamera = M3GCamera()
+        mCamera.projectionType=mCamera.PERSPECTIVE
+        mCamera.fovy=60.0 # TODO: Calculate fovy from Blender.lens 
+        mCamera.AspectRatio=4.0/3.0 # TODO: different in every device 
+        mCamera.near=camera.getClipStart()
+        mCamera.far=camera.getClipEnd()
+        self.translateToNode(obj,mCamera)
+        self.world.activeCamera = mCamera # Last one is always the active one
+    
+    
+    def translateMaterials(self, aMaterial, aMesh, aMatIndex, createNormals, createUvs):
+        print "translate materials ..."
+        
+        mAppearance = M3GAppearance()
+        
+        if createNormals:
+            mMaterial = M3GMaterial()
+            mMaterial.name = aMaterial.name
+            mMaterial.diffuseColor = self.translateRGBA(aMaterial.rgbCol,1.0) #ColorRGBA 
+            #material.specularColor= self.translateRGB(mat.specCol) #ColorRGB
+            mAppearance.material = mMaterial
+
+        if createUvs:
+            # Search file name in mesh face.
+            lImage = None
+            for iface in aMesh.faces:
+                if iface.mat==aMatIndex:
+                    if iface.image != None:
+                        lImage = iface.image
+                        break
+            if lImage==None:
+                raise Exception("Mesh " + aMesh.name + ": No image found for uv-texture! Perhaps no uv-coordinates ?")
+
+            # M3G requires textures to have power-of-two dimensions.
+            [width, height] = lImage.getSize()
+            powerWidth = 1
+            while (powerWidth < width):
+                powerWidth *= 2
+            powerHeight = 1
+            while (powerHeight < height):
+                powerHeight *= 2
+            if powerWidth != width or powerHeight != height:
+                raise Exception("Image " + lImage.filename + ": width and height must be power-of-two!")
+                
+            # ImageFactory reuses existing images.
+            mImage = ImageFactory.getImage(lImage, mOptions.textureExternal)
+            mTexture = M3GTexture2D(mImage)
+            mAppearance.textures.append(mTexture)
+
+        mPolygonMode=M3GPolygonMode()
+        mPolygonMode.perspectiveCorrectionEnabled = mOptions.perspectiveCorrection
+        if not aMesh.mode & Modes.TWOSIDED:
+            mPolygonMode.culling=M3GPolygonMode.CULL_BACK
+        else:
+            mPolygonMode.culling=M3GPolygonMode.CULL_NONE 
+        if mOptions.smoothShading:
+            mPolygonMode.shading=M3GPolygonMode.SHADE_SMOOTH
+        else:
+            mPolygonMode.shading=M3GPolygonMode.SHADE_FLAT
+        
+        mAppearance.polygonMode = mPolygonMode
+        
+        return mAppearance
+
+
+    def translateMesh(self,obj):
+        print "translate mesh ..." + str(obj)
+
+        # Mesh data.
+        mesh = obj.getData(False, True)          # get Mesh not NMesh object
+        if len(mesh.faces) <= 0:                 # no need to process empty meshes
+            print "Empty mesh " + str(obj) + " not processed."
+            return
+            
+        vertexBuffer = M3GVertexBuffer()
+        positions = M3GVertexArray(3, 2)         # 3 coordinates - 2 bytes
+        if mOptions.autoscaling: positions.useMaxPrecision(mesh.verts)
+        indexBuffers = []
+        appearances = []
+        print str(len(mesh.materials)) + " material(s) found."
+        
+        # Texture coordinates.
+        createUvs = False
+        if mOptions.textureEnabled & mesh.faceUV:
+            for material in mesh.materials:
+                if material.getMode() & Material.Modes.TEXFACE: createUvs = True;
+            
+        if createUvs:
+            if mOptions.autoscaling:
+                uvCoordinates = M3GVertexArray(2,2,True,True) #2 coordinates - 2 bytes - autoscaling
+            else:
+                uvCoordinates = M3GVertexArray(2, 2) #2 coordinates - 2 bytes
+                uvCoordinates.bias[0] = 0.5
+                uvCoordinates.bias[1] = 0.5
+                uvCoordinates.bias[2] = 0.5
+                uvCoordinates.scale = 1.0/65535.0
+        else:
+            uvCoordinates = None
+
+        # Normals.            
+        createNormals = False    
+        if mOptions.lightingEnabled:
+            for material in mesh.materials:
+                if not (material.getMode() & Material.Modes.SHADELESS): createNormals = True;
+
+        if createNormals:
+            normals = M3GVertexArray(3, 1)       # 3 coordinates - 1 byte
+        else:
+            normals = None
+        
+        # Create a submesh for each material. 
+        for materialIndex, material in enumerate(mesh.materials):
+            faces = [face for face in mesh.faces if face.mat == materialIndex]
+            if len(faces) >= 0:
+                indexBuffers.append(self.translateFaces(faces, positions, normals, uvCoordinates, createNormals, createUvs))
+                appearances.append(self.translateMaterials(material, mesh, materialIndex, createNormals, createUvs))
+                
+        # If the above didn't result in any IndexBuffer (e.g. there's no material), write a single IndexBuffer 
+        # with all faces and a default Appearance.
+        if len(indexBuffers) == 0: 
+            indexBuffers.append(self.translateFaces(mesh.faces, positions, normals, uvCoordinates, createNormals, createUvs))
+            appearances.append(M3GAppearance())
+
+        vertexBuffer.setPositions(positions)
+        if createNormals: vertexBuffer.normals = normals
+        if createUvs: vertexBuffer.texCoordArrays.append(uvCoordinates)
+
+        parent = obj.getParent()
+        if  parent!=None and parent.getType()=='Armature': #Armatures ?
+            mMesh = M3GSkinnedMesh(vertexBuffer,indexBuffers,appearances)
+            #print"vertexBuffer.positions:",vertexBuffer.positions
+            print"mMesh.vertexBuffer:",mMesh.vertexBuffer
+            self.translateArmature(parent,obj,mMesh)
+        else:
+            mMesh = M3GMesh(vertexBuffer,indexBuffers,appearances)
+            
+        self.translateToNode(obj,mMesh)
+        
+        #Do Animation
+        self.translateObjectIpo(obj,mMesh)  
+        
+    def translateFaces(self, faces, positions, normals, uvCoordinates, createNormals, createUvs):
+        """Translates a list of faces into vertex data and triangle strips."""
+        
+        # Create vertices and triangle strips.
+        indices = [0, 0, 0, 0]
+        triangleStrips = M3GTriangleStripArray()
+        
+        for face in faces:
+            for vertexIndex, vertex in enumerate(face.verts):
+                # Find candidates for sharing (vertices with same Blender ID).
+                vertexCandidateIds = [int(k) for k, v in positions.blenderIndexes.items() if v == vertex.index]
+
+                # Check normal.
+                if createNormals and not face.smooth:
+                    # For solid faces, a vertex can only be shared if the the face normal is 
+                    # the same as the normal of the shared vertex.
+                    for candidateId in vertexCandidateIds[:]:
+                        for j in range(3):
+                            if face.no[j]*127 != normals.components[candidateId*3 + j]:
+                                vertexCandidateIds.remove(candidateId)
+                                break
+
+                # Check texture coordinates.
+                if createUvs:
+                    # If texture coordinates are required, a vertex can only be shared if the 
+                    # texture coordinates match.
+                    for candidateId in vertexCandidateIds[:]:
+                        s = int((face.uv[vertexIndex][0]-0.5)*65535)
+                        t = int((0.5-face.uv[vertexIndex][1])*65535)
+                        if (s != uvCoordinates.components[candidateId*2 + 0]) or (t != uvCoordinates.components[candidateId*2 + 1]):
+                            vertexCandidateIds.remove(candidateId)
+
+                if len(vertexCandidateIds) > 0:
+                    # Share the vertex.
+                    indices[vertexIndex] = vertexCandidateIds[0]
+                else:
+                    # Create new vertex.
+                    positions.append(vertex, vertex.index)
+                    indices[vertexIndex] = len(positions.components)/3 - 1
+
+                    # Normal.
+                    if createNormals:
+                        for j in range(3):
+                            if face.smooth:
+                                normals.append(int(vertex.no[j]*127))    # vertex normal
+                            else:
+                                normals.append(int(face.no[j]*127))      # face normal
+
+                    # Texture coordinates.
+                    if createUvs:
+                        lUvCoordinatesFound = True
+                        print "face.uv[vertexIndex][0]:",face.uv[vertexIndex][0]
+                        print "face.uv[vertexIndex][1]:",face.uv[vertexIndex][1]
+                        if mOptions.autoscaling:
+                            uvCoordinates.append(face.uv[vertexIndex][0])
+                            uvCoordinates.append(face.uv[vertexIndex][1])
+                        else:
+                            uvCoordinates.append(int((face.uv[vertexIndex][0]-0.5)*65535))
+                            # Reverse t coordinate because M3G uses a different 2D coordinate system than Blender.
+                            uvCoordinates.append(int((0.5-face.uv[vertexIndex][1])*65535))
+
+            # IndexBuffer.
+            triangleStrips.stripLengths.append(len(face.verts)) 
+            if len(face.verts) > 3 :
+                triangleStrips.indices += [indices[1], indices[2], indices[0], indices[3]]     # quad
+            else :
+                triangleStrips.indices += [indices[0], indices[1], indices[2]]                 # tri
+                
+        return triangleStrips
+        
+        
+    def translateObjectIpo(self,obj,aM3GObject):
+        if obj.getIpo() == None : return #No Ipo available
+        print "translate Ipo ..."
+        
+        lIpo = obj.getIpo()
+        self.translateIpo(lIpo,aM3GObject)
+        
+        
+    def translateIpo(self,aIpo,aM3GObject,aM3GAnimContr=None,aEndFrame=0):    
+        #Print info about curves   
+        #for iCurve in lIpo.getCurves():
+        #    print "Extrapolation",iCurve.getExtrapolation() #Constant, Extrapolation, Cyclic or Cyclic_extrapolation
+        #    print "Interpolation",iCurve.getInterpolation() #Constant, Bezier, or Linear
+        #    print "Name",iCurve.getName()
+        #    for iPoint in iCurve.getPoints():
+        #        print "Knode points",iPoint.getPoints()
+        types = ['Loc','Rot','Size','Quat']
+        
+        for type in types:
+            if aIpo.getCurve(type+'X'):
+                self.translateIpoCurve(aIpo,aM3GObject,type,aM3GAnimContr,aEndFrame)
+
+
+    def translateIpoCurve(self,aIpo,aM3GObject,aCurveType,aM3GAnimContr,aEndFrame=0):
+        
+        lContext = self.scene.getRenderingContext()
+        if aEndFrame==0: 
+            lEndFrame = lContext.endFrame()
+        else:
+            lEndFrame = aEndFrame
+            
+        lTimePerFrame = 1.0 / lContext.framesPerSec() * 1000 
+        
+        lCurveX = aIpo.getCurve(aCurveType+'X')
+        lCurveY = aIpo.getCurve(aCurveType+'Y')
+        lCurveZ = aIpo.getCurve(aCurveType+'Z')
+        if aCurveType=='Quat': lCurveW = aIpo.getCurve(aCurveType+'W')
+        
+        lInterpolation = None
+        if aCurveType == 'Rot' or aCurveType == 'Quat':
+            lTrackType = M3GAnimationTrack.ORIENTATION
+            lNumComponents=4
+            lCurveFactor= 10 #45 Degrees = 4,5
+        if aCurveType == 'Quat':
+            lTrackType = M3GAnimationTrack.ORIENTATION
+            lNumComponents=4
+            lCurveFactor= 1
+            lInterpolation = M3GKeyframeSequence.SLERP
+            #lInterpolation = M3GKeyframeSequence.SQUAD
+        elif aCurveType == 'Size':
+            lTrackType = M3GAnimationTrack.SCALE
+            lNumComponents=3
+            lCurveFactor=1
+        else:
+            lTrackType = M3GAnimationTrack.TRANSLATION
+            lNumComponents=3
+            lCurveFactor=1
+            
+        mSequence = M3GKeyframeSequence(len(lCurveX.getPoints()),
+                                        lNumComponents,
+                                        lCurveX.getInterpolation(),
+                                        lInterpolation)
+
+        #print 'ComponentCount',mSequence.componentCount
+        
+        mSequence.duration = lEndFrame * lTimePerFrame
+        mSequence.setRepeatMode(lCurveX.getExtrapolation())
+        
+        lIndex = 0
+        for iPoint in lCurveX.getPoints():
+            lPoint = iPoint.getPoints()
+             
+            lPointList = [(lPoint[1]*lCurveFactor),
+                          (lCurveY.evaluate(lPoint[0])*lCurveFactor),
+                          (lCurveZ.evaluate(lPoint[0])*lCurveFactor)]
+                        
+            #print "aCurveType ", aCurveType
+            
+            if aCurveType == 'Loc':
+                #print "PointList ", lPointList
+                #lorgTransVector = aM3GObject.blenderTransformMatrix.translationPart()
+                #ltrans = TranslationMatrix(Vector(lPointList))
+                #ltrans2 = self.calculateChildMatrix(ltrans,aM3GObject.blenderTransformMatrix)
+                #lVector = ltrans2.translationPart() + lorgTransVector
+                #lPointList = [lVector.x, lVector.y,lVector.z] 
+                #print "PointList ", lPointList
+                pass
+                           
+            if aCurveType == 'Quat':
+                lPointList.append(lCurveW.evaluate(lPoint[0])*lCurveFactor)
+                #lQuat = Quaternion([lPointList[3],lPointList[0],lPointList[1],lPointList[2]])
+                #print "Quat ", lQuat
+                #print "Quat.angel ", lQuat.angle
+                #print "Quat.axis ", lQuat.axis
+                #print "PointList ", lPointList
+                
+            #print "PointList",lPointList
+                                
+            if aCurveType =='Rot':
+                lQuat = Euler(lPointList).toQuat()
+                #lPointList = [lQuat.w,lQuat.x,lQuat.y,lQuat.z]
+                lPointList = [lQuat.x,lQuat.y,lQuat.z,lQuat.w]
+                #print " Quat=", lPointList
+        
+            mSequence.setKeyframe(lIndex,
+                                  lPoint[0]*lTimePerFrame, 
+                                  lPointList)
+            lIndex += 1
+        mSequence.validRangeFirst = 0 
+        mSequence.validRangeLast = lIndex - 1  
+        
+        mTrack = M3GAnimationTrack(mSequence,lTrackType)
+        aM3GObject.animationTracks.append(mTrack)
+        if aM3GAnimContr==None:  aM3GAnimContr = M3GAnimationController()
+        mTrack.animationController = aM3GAnimContr
+        
+        
+    def translateLamp(self,obj):
+        print "translate lamp ..."
+        lamp = obj.getData()
+        
+        #Type
+        lampType=lamp.getType()
+        if not lampType in [Lamp.Types.Lamp,Lamp.Types.Spot,Lamp.Types.Sun]:
+            print "INFO: Type of light is not supported. See documentation"
+            return #create not light; type not supported
+        mLight = M3GLight()
+        if lampType == Lamp.Types.Lamp:
+            mLight.mode = mLight.modes['OMNI']
+        elif lampType == Lamp.Types.Spot:
+            mLight.mode = mLight.modes['SPOT']
+        elif lampType == Lamp.Types.Sun:
+            mLight.mode = mLight.modes['DIRECTIONAL']
+        #Attenuation (OMNI,SPOT):
+        if lampType in [Lamp.Types.Lamp,Lamp.Types.Spot]:
+            mLight.attenuationConstant = 0.0
+            mLight.attenuationLinear = 2.0/lamp.dist 
+            mLight.attenuationQuadratic = 0.0 
+        #Color
+        mLight.color = self.translateRGB(lamp.col)        
+        #Energy  
+        mLight.intensity = lamp.energy
+        #SpotAngle, SpotExponent (SPOT)
+        if lampType == Lamp.Types.Spot:
+            mLight.spotAngle = lamp.spotSize 
+            mLight.spotExponent = lamp.spotBlend 
+        self.translateToNode(obj,mLight)
+
+
+    def translateCore(self,obj,node):
+        #Name
+        node.name = obj.name
+        node.userID = self.translateUserID(obj.name)
+        #Location
+        #node.translation=self.translateLoc(obj.LocX,obj.LocY,obj.LocZ
+        #node.hasComponentTransform=True
+        #Transform
+        #node.transform = self.translateMatrix(obj.getMatrix('localspace'))
+        if type(obj) is Types.BoneType:
+            #print "BoneMatrix ",obj.matrix['BONESPACE']
+            node.transform = self.translateMatrix(obj.matrix['ARMATURESPACE'])
+            #'ARMATURESPACE' - this matrix of the bone in relation to the armature 
+            #'BONESPACE' - the matrix of the bone in relation to itself
+        else:
+            node.transform = self.translateMatrix(obj.matrixWorld)
+        node.hasGeneralTransform=True
+        
+        
+    def translateToNode(self,obj,node):
+        self.translateCore(obj,node)
+        #Nodes
+        self.nodes.append(node)
+        #Link to Blender Object
+        node.blenderObj = obj
+        node.blenderMatrixWorld = obj.matrixWorld
+        lparent = None
+        if obj.getParent()!=None:
+            if obj.getParent().getType()!='Armature':
+                lparent = obj.getParent()
+            else:
+                if obj.getParent().getParent()!=None and obj.getParent().getParent().getType()!='Armature':
+                    lparent = obj.getParent().getParent()
+        node.parentBlenderObj = lparent
+        
+        
+    def translateUserID(self, name):
+        id = 0
+        start = name.find('#')
+        
+        # Use digits that follow the # sign for id.
+        if start != -1:
+            start += 1
+            end = start
+            for char in name[start:]:
+                if char.isdigit():
+                    end += 1
+                else:
+                    break
+                    
+            if end > start:
+                id = int(name[start:end])
+        
+        return id        
+        
+    def translateLoc(self,aLocX,aLocY,aLocZ):
+        return M3GVector3D(aLocX,aLocY,aLocZ)
+        
+    def translateRGB(self,color):
+        return M3GColorRGB(int(color[0]*255),
+                            int(color[1]*255), 
+                            int(color[2]*255))
+    
+    def translateRGBA(self,color,alpha):
+        return M3GColorRGBA(int(color[0]*255),
+                            int(color[1]*255), 
+                            int(color[2]*255),
+                            int(alpha*255))
+    
+    def translateMatrix(self,aPyMatrix):
+        """
+         0   1   2   3 
+         4   5   6   7 
+         8   9  10  11
+         12  13  14  15 """
+        #print "Matrix:", aPyMatrix
+        lMatrix = M3GMatrix()
+        lMatrix.elements[0] = aPyMatrix[0][0]
+        lMatrix.elements[1] = aPyMatrix[1][0]
+        lMatrix.elements[2] = aPyMatrix[2][0]
+        lMatrix.elements[3] = aPyMatrix[3][0]
+        lMatrix.elements[4] = aPyMatrix[0][1]
+        lMatrix.elements[5] = aPyMatrix[1][1]
+        lMatrix.elements[6] = aPyMatrix[2][1]
+        lMatrix.elements[7] = aPyMatrix[3][1]
+        lMatrix.elements[8] = aPyMatrix[0][2]
+        lMatrix.elements[9] = aPyMatrix[1][2]
+        lMatrix.elements[10] = aPyMatrix[2][2]
+        lMatrix.elements[11] = aPyMatrix[3][2]
+        lMatrix.elements[12] = aPyMatrix[0][3]
+        lMatrix.elements[13] = aPyMatrix[1][3]
+        lMatrix.elements[14] = aPyMatrix[2][3]
+        lMatrix.elements[15] = aPyMatrix[3][3]
+        return lMatrix
+        
+    
+# ---- Exporter ---------------------------------------------------------------- #
+
+class M3GExporter:
+    "Exports Blender-Scene to M3D"
+    def __init__(self, aWriter): 
+        self.writer = aWriter
+
+        
+    def start(self):
+        print "Info: starting export ..."
+        #rpdb2.start_embedded_debugger("t",True)
+        Translator = M3GTranslator()
+        world = Translator.start()
+        
+        #sys.settrace(tracer)
+        exportList = self.createDeepSearchList(world)
+        externalReferences = [element for element in exportList if element.__class__ is M3GExternalReference]
+        exportList = [element for element in exportList if element.__class__ is  not M3GExternalReference]
+        #sys.settrace(None)
+        
+        # 1 is reservated for HeaderObject.
+        i=1 
+        
+        # Next are the external references.
+        for element in externalReferences:
+            i += 1
+            element.id = i
+            print "object ",element.id, element
+            
+        # And the standard scene objects.
+        for element in exportList:
+            i += 1
+            element.id = i
+            print "object ",element.id, element
+            
+        self.writer.writeFile(world, exportList, externalReferences)
+        
+        print("Ready!")
+
+
+    def createDeepSearchList(self,aWorld):
+        "creates the right order for saving m3g : leafs first"
+        return aWorld.searchDeep([])
+         
+   
+       
+# ---- Writer ---------------------------------------------------------------- #   
+class JavaWriter:
+    "writes a java class which creates m3g-Scene in a j2me programm"
+    def __init__(self,aFilename):
+        self.filename = aFilename
+        self.classname = Blender.sys.basename(aFilename)
+        self.classname = self.classname[:-5] #without extention ".java"
+        self.outFile = file(aFilename,"w")
+        
+    def write(self, tab, zeile=""):
+        "writes to file"
+        #print "\t" * tab + zeile
+        print >>self.outFile, "\t" * tab + zeile
+
+    def writeFile(self,aWorld,aExportList,externalReferences):
+        self.world = aWorld
+        self.writeHeader()
+        for element in aExportList:
+            element.writeJava(self,True)
+        self.writeFooter()
+        self.outFile.close()
+        
+    def writeHeader(self):
+        "writes class header"
+        self.write(0,"import javax.microedition.lcdui.Image;")
+        self.write(0,"import javax.microedition.m3g.*;")
+        self.write(0,"public final class "+self.classname+" {")
+        self.write(1,"public static World getRoot(Canvas3D aCanvas) {")
+          
+    def writeFooter(self):
+        self.write(1)
+        self.write(1,"return BL"+str(self.world.id)+";")
+        self.write(0,"}}")
+        
+    def writeList(self,alist,numberOfElementsPerLine=12,aType=""):
+        '''Writes numberOfElementsPerLine'''
+        line=""
+        lastLine=""
+        counter=0
+        for element in alist:
+            if counter!=0:
+                line = line + "," + str(element) + aType
+            else:
+                line = str(element) + aType
+            counter = counter + 1
+            if counter == numberOfElementsPerLine:
+                if len(lastLine)>0:
+                    self.write(3,lastLine+",")
+                lastLine=line
+                line=""
+                counter = 0
+        if len(lastLine)>0:
+            if len(line)>0:
+                self.write(3,lastLine+",")
+            else:
+                self.write(3,lastLine)
+        if len(line) > 0: self.write(3,line)
+    
+    def writeClass(self,aName,aM3GObject):
+        self.write(2)
+        self.write(2,"//"+aName+":"+aM3GObject.name)
+      
+
+class M3GSectionObject:
+    def __init__(self,aObject):
+        """Object Structure
+           Each object in the file represents one object in the 
+           scene graph tree, and is stored in a chunk. The
+           structure of an object chunk is as follows:
+               Byte ObjectType
+               UInt32 Length
+               Byte[] Data"""
+        #ObjectType
+        #This field describes what type of object has been serialized.
+        #The values 0 and 0xFF are special: 0 represents the header object, 
+        #and 0xFF represents an external reference.
+        #Example: Byte ObjectType = 14
+        self.ObjectType = aObject.ObjectType
+        self.data = aObject.getData()
+        self.length = aObject.getDataLength()
+    
+    def getData(self):
+        data = struct.pack('<BI',self.ObjectType,self.length)
+        data += self.data
+        return data
+    
+    def getDataLength(self):
+        return struct.calcsize('<BI') + self.length
+        
+class M3GSection:
+    '''Part of a M3G binary file'
+       Section Structur
+           Byte CompressionScheme
+           UInt32 TotalSectionLength
+           UInt32 UncompressedLength
+           Byte[] Objects
+           UInt32 Checksum '''
+    def __init__(self,aObjectList,compressed=False):
+        self.CompressionScheme=0
+        self.TotalSectionLength=0 #including the CompressionScheme, 
+                                  #TotalSectionLength, UncompressedLength,
+                                  #Objects and Checksum fields,
+        self.UncompressedLength=0 #Length of Objects uncompresses
+        self.Objects = ''
+        for element in aObjectList:
+            lObject = M3GSectionObject(element)
+            #print "Obj:", lObject, lObject.getDataLength(), len(lObject.getData())
+            self.Objects += lObject.getData()
+            self.UncompressedLength += lObject.getDataLength()
+        self.TotalSectionLength=struct.calcsize('<BIII')+self.UncompressedLength
+    
+    def getData(self):
+        data = struct.pack('<BII',self.CompressionScheme,
+                                self.TotalSectionLength,
+                                self.UncompressedLength)
+        data += self.Objects
+        #self.Checksum = zlib.adler32(data)
+        self.Checksum = self.ownAdler32(data)
+        print "Checksum",self.Checksum
+        #print "Own Checksum",self.ownAdler32(data)
+        return data + struct.pack('<I',self.Checksum)
+    
+    def ownAdler32(self,data):
+        s1 = int(1) #uint32_t
+        s2 = int(0) #uint32_t
+        for n in data:
+            s1 = (s1 + int(struct.unpack("B",n)[0])) % 65521
+            s2 = (s2 + s1) % 65521
+        return (s2 << 16) + s1
+    
+    def getLength(self):
+        return self.TotalSectionLength
+        
+    def write(self,aFile):
+        print "Write Section.."
+        print "TotalSectionLength:", str(self.TotalSectionLength)
+        aFile.write(self.getData())
+            
+        #CompressionScheme
+        #Example Byte CompressionScheme = 1;
+        #0 Uncompressed, Adler32 Checksum
+        #1 ZLib compression, 32 k buffer size, Adler32 Checksum
+        #2...255 Reserved
+        #TotalSectionLength: total length of the section in bytes
+        #Example: UInt32 TotalSectionLength = 2056
+        #UncompressedLength 
+        #contains the length of the Objects field after decompression. 
+        #If no compression is specified for this section,
+        #this equals the actual number of bytes serialized in the Objects array.
+        #CompressionScheme
+        #Currently, only the Adler32 checksum is mandatory. 
+        #The checksum is calculated using all preceding bytes in
+        #the section, i.e. the CompressionScheme,TotalSectionLength, 
+        #UncompressedLength, and the actual serialized data in the 
+        #Objects field (i.e. in its compressed form if compression is speci-
+        #fied).
+        #Example: UInt32 Checksum = 0xffe806a3
+    
+    
+    
+    
+class M3GFileIdentifier:
+    def __init__(self):
+        '''Byte[12] FileIdentifier = { 0xAB, 0x4A, 0x53, 0x52, 0x31, 0x38, 0x34,
+                        0xBB, 0x0D, 0x0A, 0x1A, 0x0A }
+           This can also be expressed using C-style character definitions as:
+           Byte[12] FileIdentifier = { '«', 'J', 'S', 'R', '1', '8', '4', '»', '\r',
+                        '\n', '\x1A', '\n' }'''
+        self.data = [ 0xAB, 0x4A, 0x53, 0x52, 0x31, 0x38, 0x34,
+                        0xBB, 0x0D, 0x0A, 0x1A, 0x0A ]
+    
+    def write(self,aFile):
+        for element in self.data:
+            aFile.write(struct.pack('B',element))
+        
+    def getLength(self):
+        return len(self.data)
+        
+        
+class M3GWriter:
+    """writes a m3g binary file
+      File Structur
+        File Identifier
+        Section 0 File Header Object
+        Section 1 External Reference Objects
+        Section 2 Scene Objects
+        Section 3 Scene Objects
+                ... ...
+        Section n Scene Objects"""
+        
+    def __init__(self,aFilename):
+        self.FileName = aFilename
+    
+    
+    def writeFile(self,aWorld,aExportList,externalReferences):
+        '''Called after translation
+           first atempt: all objects in one section'''
+        print "M3G file writing .."
+        
+        fileIdentifier = M3GFileIdentifier()
+        
+        fileHeaderObject = M3GHeaderObject()
+        section0 = M3GSection([fileHeaderObject])
+        sectionN = M3GSection(aExportList)
+        
+        length = fileIdentifier.getLength()
+        length += section0.getLength()
+        length += sectionN.getLength()
+        
+        if len(externalReferences) != 0:
+            section1 = M3GSection(externalReferences)
+            length += section1.getLength()
+            fileHeaderObject.hasExternalReferences = True
+        
+        fileHeaderObject.TotalFileSize=length 
+        fileHeaderObject.ApproximateContentSize=length
+        section0 = M3GSection([fileHeaderObject])
+       
+        output = open(self.FileName, mode='wb')
+
+        fileIdentifier.write(output)
+        section0.write(output)
+        if len(externalReferences) != 0:
+            section1.write(output)
+        sectionN.write(output)
+        
+        output.close()
+
+        print "M3G file written."
+   
+class OptionMgr:
+    """Reads and saves options """
+
+    def __init__(self):
+        self.setDefault()
+        rdict = Registry.GetKey('M3GExport', True) # True to check on disk as well
+        if rdict: # if found, get the values saved there
+            try:
+                self.textureEnabled        = rdict['textureEnabled']
+                self.textureExternal       = rdict['textureExternal']
+                self.lightingEnabled       = rdict['lightingEnabled']
+                self.createAmbientLight    = rdict['createAmbientLight']
+                self.exportAllActions      = rdict['exportAllActions']
+                self.autoscaling           = rdict['autoscaling']
+                self.perspectiveCorrection = rdict['perspectiveCorrection']
+                self.smoothShading         = rdict['smoothShading']
+                self.exportAsJava          = rdict['exportAsJava']
+                
+            except: self.save()     # if data isn't valid, rewrite it
+
+    def setDefault(self):
+        self.textureEnabled         = True
+        self.textureExternal        = False
+        self.lightingEnabled        = True
+        self.createAmbientLight     = False
+        self.exportAllActions       = False
+        self.autoscaling            = True
+        self.perspectiveCorrection  = False
+        self.smoothShading          = True
+        self.exportAsJava           = False
+    
+    def save(self):
+        d = {}
+        d['textureEnabled']        = self.textureEnabled
+        d['textureExternal']       = self.textureExternal
+        d['lightingEnabled']       = self.lightingEnabled
+        d['createAmbientLight']    = self.createAmbientLight
+        d['exportAllActions']      = self.exportAllActions
+        d['autoscaling']           = self.autoscaling
+        d['perspectiveCorrection'] = self.perspectiveCorrection
+        d['smoothShading']         = self.smoothShading
+        d['exportAsJava']          = self.exportAsJava
+
+        Blender.Registry.SetKey('M3GExport', d, True)
+
+
+# ---- User Interface -------------------------------------------------------- #
+mOptions = OptionMgr()
+
+def gui():              
+    """Draws the options menu."""
+    # Flush events.
+    for s in Window.GetScreenInfo():
+        Window.QHandle(s['id'])
+        
+    # Display options.
+    textureEnabled         = Draw.Create(mOptions.textureEnabled)
+    textureExternal        = Draw.Create(mOptions.textureExternal)
+    lightingEnabled        = Draw.Create(mOptions.lightingEnabled)
+    createAmbientLight     = Draw.Create(mOptions.createAmbientLight)
+    exportAllActions       = Draw.Create(mOptions.exportAllActions)
+    autoscaling            = Draw.Create(mOptions.autoscaling)
+    perspectiveCorrection  = Draw.Create(mOptions.perspectiveCorrection)
+    smoothShading          = Draw.Create(mOptions.smoothShading)
+    exportAsJava           = Draw.Create(mOptions.exportAsJava)
+    
+    pupBlock = [\
+        ('Texturing'),\
+        ('Enabled',              textureEnabled,         'Enables texture export'),\
+        ('External',             textureExternal,        'References external files for textures'),\
+        ('Lighting'),\
+        ('Enabled',              lightingEnabled,        'Enables light export'),\
+        ('Ambient Light',        createAmbientLight,     'Inserts an extra light object for ambient light'),\
+        ('Mesh Options'),\
+        ('Autoscaling',          autoscaling,            'Uses maximum precision for vertex positions'),\
+        ('Persp. Correction',    perspectiveCorrection,  'Sets perspective correction flag'),\
+        ('Smooth Shading',       smoothShading,          'Sets smooth shading flag'),\
+        ('Posing'),\
+        ('All Armature Actions', exportAllActions,       'Exports all actions for armatures'),\
+        ('Export'),\
+        ('As Java Source',       exportAsJava,           'Exports scene as Java source code')
+    ]
+    
+    # Only execute if use didn't quit (ESC).
+    if Draw.PupBlock('M3G Export', pupBlock):
+        mOptions.textureEnabled         = textureEnabled.val
+        mOptions.textureExternal        = textureExternal.val
+        mOptions.lightingEnabled        = lightingEnabled.val
+        mOptions.createAmbientLight     = createAmbientLight.val
+        mOptions.exportAllActions       = exportAllActions.val
+        mOptions.autoscaling            = autoscaling.val
+        mOptions.perspectiveCorrection  = perspectiveCorrection.val
+        mOptions.smoothShading          = smoothShading.val
+        mOptions.exportAsJava           = exportAsJava.val
+        mOptions.save()
+
+        if mOptions.exportAsJava:
+            Window.FileSelector(file_callback_java, 'Export M3G as Java', Blender.sys.makename(ext='.java'))
+        else:
+            Window.FileSelector(file_callback_m3g,  'Export M3G Binary',  Blender.sys.makename(ext='.m3g'))
+        
+def file_callback_java(filename):
+    Window.WaitCursor(1)    # Blender will automatically remove wait cursor in case of an exception
+    exporter=M3GExporter(JavaWriter(filename))
+    exporter.start()
+    Window.WaitCursor(0)
+    Window.RedrawAll()
+
+def file_callback_m3g(filename):
+    Window.WaitCursor(1)
+    exporter=M3GExporter(M3GWriter(filename))
+    exporter.start()
+    Window.WaitCursor(0)
+    Window.RedrawAll()
+    
+if __name__ == '__main__':
+    gui()
\ No newline at end of file