Category: copy/paste UVs
[blender-addons-contrib.git] / io_export_marmalade.py
index 36ccfc36d4242f14e0210bbaf1584f1882315dd4..adf5448cfec0402ef76cd7ce93cec7aa9b2ddba0 100644 (file)
 bl_info = {
     "name": "Marmalade Cross-platform Apps (.group)",
     "author": "Benoit Muller",
-    "version": (0, 5, 1),
-    "blender": (2, 6, 0),
+    "version": (0, 6, 2),
+    "blender": (2, 63, 0),
     "location": "File > Export > Marmalade cross-platform Apps (.group)",
     "description": "Export Marmalade Format files (.group)",
     "warning": "",
-    "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"\
+    "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
         "Scripts/Import-Export/Marmalade_Exporter",
-    "tracker_url": "https://projects.blender.org/tracker/index.php?"\
-        "",
+    "tracker_url": "https://developer.blender.org",
     "category": "Import-Export"}
 
 import os
@@ -54,7 +53,6 @@ class MarmaladeExporterSettings:
     def __init__(self,
                  context,
                  FilePath,
-                 Optimized=True,
                  CoordinateSystem=1,
                  FlipNormals=False,
                  ApplyModifiers=False,
@@ -65,13 +63,13 @@ class MarmaladeExporterSettings:
                  ExportTextures=True,
                  CopyTextureFiles=True,
                  ExportArmatures=False,
-                 ExportAnimation=0,
+                 ExportAnimationFrames=0,
+                 ExportAnimationActions=0,
                  ExportMode=1,
                  MergeModes=0,
                  Verbose=False):
         self.context = context
         self.FilePath = FilePath
-        self.Optimized = Optimized
         self.CoordinateSystem = int(CoordinateSystem)
         self.FlipNormals = FlipNormals
         self.ApplyModifiers = ApplyModifiers
@@ -82,7 +80,8 @@ class MarmaladeExporterSettings:
         self.ExportTextures = ExportTextures
         self.CopyTextureFiles = CopyTextureFiles
         self.ExportArmatures = ExportArmatures
-        self.ExportAnimation = int(ExportAnimation)
+        self.ExportAnimationFrames = int(ExportAnimationFrames)
+        self.ExportAnimationActions = int(ExportAnimationActions)
         self.ExportMode = int(ExportMode)
         self.MergeModes = int(MergeModes)
         self.Verbose = Verbose
@@ -98,10 +97,6 @@ def ExportMadeWithMarmaladeGroup(Config):
     if Config.Verbose:
         print("Done")
 
-    if Config.MergeModes > 0:
-        # Merge mode only work with Optimised setting
-        Config.Optimized = True
-
     if Config.Verbose:
         print("writing group header")
 
@@ -126,15 +121,14 @@ def ExportMadeWithMarmaladeGroup(Config):
     if Config.Verbose:
         print("Setting up...")
 
-    if Config.ExportAnimation:
+    if Config.ExportAnimationFrames:
         if Config.Verbose:
             print(bpy.context.scene)
             print(bpy.context.scene.frame_current)
         CurrentFrame = bpy.context.scene.frame_current
-        #comment because it crashes Blender on some old blend file: bpy.context.scene.frame_current = bpy.context.scene.frame_current
     if Config.Verbose:
         print("Done")
-    
+
     Config.ObjectList = []
     if Config.Verbose:
         print("Writing Objects...")
@@ -145,7 +139,7 @@ def ExportMadeWithMarmaladeGroup(Config):
     if Config.Verbose:
         print("Objects Exported: {}".format(Config.ExportList))
 
-    if Config.ExportAnimation:
+    if Config.ExportAnimationFrames:
         if Config.Verbose:
             print("Writing Animation...")
         WriteKeyedAnimationSet(Config, bpy.context.scene)
@@ -162,14 +156,6 @@ def GetObjectChildren(Parent):
             if Object.type in {'ARMATURE', 'EMPTY', 'MESH'}]
 
 
-#Returns the vertex count of Mesh in not optimized version, counting each vertex for every face.
-def GetNonOptimizedMeshVertexCount(Mesh):
-    VertexCount = 0
-    for Face in Mesh.faces:
-        VertexCount += len(Face.vertices)
-    return VertexCount
-
-
 #Returns the file path of first image texture from Material.
 def GetMaterialTextureFullPath(Config, Material):
     if Material:
@@ -211,20 +197,20 @@ def WriteObjects(Config, ObjectList, geoFile=None, mtlFile=None, GeoModel=None,
     for Object in ObjectList:
         if Config.Verbose:
             print("  Writing Object: {}...".format(Object.name))
-        
-        if Config.ExportArmatures and Object.type == "ARMATURE":           
+
+        if Config.ExportArmatures and Object.type == "ARMATURE":
             Armature = Object.data
             ParentList = [Bone for Bone in Armature.bones if Bone.parent is None]
             if Config.Verbose:
                 print("    Writing Armature Bones...")
             #Create the skel file
-            skelfullname = os.path.dirname(Config.FilePath) + "\models\%s.skel" % (StripName(Object.name))
+            skelfullname = os.path.dirname(Config.FilePath) + os.sep + "models" + os.sep + "%s.skel" % (StripName(Object.name))
             ensure_dir(skelfullname)
             if Config.Verbose:
                 print("      Creating skel file %s" % (skelfullname))
 
             skelFile = open(skelfullname, "w")
-            skelFile.write('// skel file exported from : %r\n' % os.path.basename(bpy.data.filepath))   
+            skelFile.write('// skel file exported from : %r\n' % os.path.basename(bpy.data.filepath))
             skelFile.write("CIwAnimSkel\n")
             skelFile.write("{\n")
             skelFile.write("\tnumBones %d\n" % (len(Armature.bones)))
@@ -279,21 +265,21 @@ def WriteObjects(Config, ObjectList, geoFile=None, mtlFile=None, GeoModel=None,
                 scalematrix[1][1] = meshScale.y * Config.Scale
                 scalematrix[2][2] = meshScale.z * Config.Scale
 
-                Mesh.transform(scalematrix * X_ROT)
+                meshRot = Object.matrix_world.to_quaternion()  # Export is working, even if user doesn't have use apply Rotation in Edit mode.
+                Mesh.transform(X_ROT * meshRot.to_matrix().to_4x4() * scalematrix)
             else:
                 # In Merge mode, we need to keep relative postion of each objects, so we export in WORLD SPACE
                 SCALE_MAT = mathutils.Matrix.Scale(Config.Scale, 4)
-                Mesh.transform(Object.matrix_world * SCALE_MAT * X_ROT)
+                Mesh.transform(SCALE_MAT * X_ROT * Object.matrix_world)
 
              # manage merge options
-   
+
             if Config.MergeModes == 0:
                 #one geo per Object, so use name of Object for the Geo file
                 geoFile, mtlFile = CreateGeoMtlFiles(Config, StripName(Object.name))
-                if Config.Optimized == True:
-                    GeoModel = CGeoModel(StripName(Object.name))  
-                
-            # Write the Mesh in the Geo file   
+                GeoModel = CGeoModel(StripName(Object.name))
+
+            # Write the Mesh in the Geo file
             WriteMesh(Config, Object, Mesh, geoFile, mtlFile, GeoModel)
 
             if Config.MergeModes == 0:
@@ -304,7 +290,7 @@ def WriteObjects(Config, ObjectList, geoFile=None, mtlFile=None, GeoModel=None,
                 GeoModel = None
             elif Config.MergeModes == 1:
                 # merge in one Mesh, so keep the Geo class and prepare to change object
-                GeoModel.NewObject() 
+                GeoModel.NewObject()
             elif Config.MergeModes == 2:
                 # merge several Meshes in one file: so clear the mesh data that we just written in the file,
                 # but keep Materials info that need to be merged across objects
@@ -327,8 +313,24 @@ def WriteObjects(Config, ObjectList, geoFile=None, mtlFile=None, GeoModel=None,
                 # we have Merges all objects in one Mesh, so time to write this big mesh in the file
                 GeoModel.PrintGeoMesh(geoFile)
                 # time to write skinfile if any
-                len(GeoModel.useBonesDict)
                 if len(GeoModel.useBonesDict) > 0:
+                    # some mesh was not modified by the armature. so we must skinned the merged mesh.
+                    # So unskinned vertices from unarmatured meshes, are assigned to the root bone of the armature
+                    for i in range(0, len(GeoModel.vList)):
+                        if not i in GeoModel.skinnedVertices:
+                            GeoModel.skinnedVertices.append(i)
+                            useBonesKey = pow(2, GeoModel.armatureRootBoneIndex)
+                            vertexGroupIndices = list((GeoModel.armatureRootBoneIndex,))
+                            if useBonesKey not in GeoModel.useBonesDict:
+                                GeoModel.mapVertexGroupNames[GeoModel.armatureRootBoneIndex] = StripBoneName(GeoModel.armatureRootBone.name)
+                                VertexList = []
+                                VertexList.append("\t\tvertWeights { %d, 1.0}" % i)
+                                GeoModel.useBonesDict[useBonesKey] = (vertexGroupIndices, VertexList)
+                            else:
+                                pair_ListGroupIndices_ListAssignedVertices = GeoModel.useBonesDict[useBonesKey]
+                                pair_ListGroupIndices_ListAssignedVertices[1].append("\t\tvertWeights { %d, 1.0}" % i)
+                                GeoModel.useBonesDict[useBonesKey] = pair_ListGroupIndices_ListAssignedVertices
+                    # now generates the skin file
                     PrintSkinWeights(Config, GeoModel.armatureObjectName, GeoModel.useBonesDict, GeoModel.mapVertexGroupNames, GeoModel.name)
             if Config.MergeModes > 0:
                 WriteMeshMaterialsForGeoModel(Config, mtlFile, GeoModel)
@@ -340,10 +342,10 @@ def WriteObjects(Config, ObjectList, geoFile=None, mtlFile=None, GeoModel=None,
 
 def CreateGeoMtlFiles(Config, Name):
     #Create the geo file
-    geofullname = os.path.dirname(Config.FilePath) + ("\models\%s.geo" % Name)
+    geofullname = os.path.dirname(Config.FilePath) + os.sep + "models" + os.sep + "%s.geo" % Name
     ensure_dir(geofullname)
     if Config.Verbose:
-        print("      Creating geo file %s" % (geofullname))  
+        print("      Creating geo file %s" % (geofullname))
     geoFile = open(geofullname, "w")
     geoFile.write('// geo file exported from : %r\n' % os.path.basename(bpy.data.filepath))
     geoFile.write("CIwModel\n")
@@ -353,22 +355,22 @@ def CreateGeoMtlFiles(Config, Name):
     Config.File.write("\t\".\models\%s.geo\"\n" % Name)
 
     # Create the mtl file
-    mtlfullname = os.path.dirname(Config.FilePath) + "\models\%s.mtl" % (Name)
+    mtlfullname = os.path.dirname(Config.FilePath) + os.sep + "models" + os.sep + "%s.mtl" % Name
     ensure_dir(mtlfullname)
     if Config.Verbose:
         print("      Creating mtl file %s" % (mtlfullname))
     mtlFile = open(mtlfullname, "w")
-    mtlFile.write('// mtl file exported from : %r\n' % os.path.basename(bpy.data.filepath))   
+    mtlFile.write('// mtl file exported from : %r\n' % os.path.basename(bpy.data.filepath))
     return geoFile, mtlFile
 
 
 def FinalizeGeoMtlFiles(Config, geoFile, mtlFile):
     if Config.Verbose:
-        print("      Closing geo file")  
+        print("      Closing geo file")
     geoFile.write("}\n")
     geoFile.close()
     if Config.Verbose:
-        print("      Closing mtl file")  
+        print("      Closing mtl file")
     mtlFile.close()
 
 
@@ -377,63 +379,29 @@ def WriteMesh(Config, Object, Mesh,  geoFile=None, mtlFile=None, GeoModel=None):
         print (" ERROR not geo file arguments in WriteMesh method")
         return
 
-    if Config.Optimized:
-        if GeoModel == None:
-            print (" ERROR not GeoModel arguments in WriteMesh method")
-            return
+    if GeoModel == None:
+        print (" ERROR not GeoModel arguments in WriteMesh method")
+        return
+
+    BuildOptimizedGeo(Config, Object, Mesh, GeoModel)
+    if Config.MergeModes == 0 or Config.MergeModes == 2:
+        #if we don't merge, or if we write several meshes into one file ... write the mesh everytime we do an object
+        GeoModel.PrintGeoMesh(geoFile)
 
-        BuildOptimizedGeo(Config, Object, Mesh, GeoModel)
-        if Config.MergeModes == 0 or Config.MergeModes == 2:
-            #if we don't merge, or if we write several meshes into one file ... write the mesh everytime we do an object
-            GeoModel.PrintGeoMesh(geoFile)
-    else:
-        #exports not optimized by face (duplicated vertex, normals might be better in rare cases)
-        if Config.Verbose:
-            print("      Writing Mesh Vertices and normals...")
-        WriteMeshVerticesAndNormals(Config, Object, Mesh, geoFile)
-        if Config.Verbose:
-            print("      Done\n      Writing Mesh Vertices and Normals...")
-        bVertexColors = False
-        if Config.ExportVertexColors and (len(Mesh.vertex_colors) > 0):
-            if Config.Verbose:
-                print("      Writing Mesh Vertices Colors...")
-            bVertexColors = WriteMeshVerticesColors(Config, Mesh, geoFile)
-            if Config.Verbose:
-                print("      Done")
-        bUVTextures = False
-        if Config.ExportTextures and (len(Mesh.uv_textures) > 0):
-            if Config.Verbose:
-                print("      Writing Mesh UV Coordinates...")
-            bUVTextures = WriteMeshUVCoordinates(Config, Mesh, geoFile)
-            if Config.Verbose:
-                print("      Done")
-        if Config.Verbose:
-            print("      Writing Poly QuadsTris...")
-        WriteMeshPoly(Config, Mesh, geoFile, bVertexColors, bUVTextures)
-        if Config.Verbose:
-            print("      Done")
     if Config.Verbose:
         print("      Done\n      Writing Mesh Materials...")
 
     if Config.MergeModes == 0:
         #No merge, so we can diretly write the Mtl file associated to this object
-        if Config.Optimized == False:
-            WriteMeshMaterials(Config, Mesh, mtlFile)
-            geoFile.write("\t}\n")
-        else:
-            WriteMeshMaterialsForGeoModel(Config, mtlFile, GeoModel)
+        WriteMeshMaterialsForGeoModel(Config, mtlFile, GeoModel)
 
     if Config.Verbose:
         print("      Done")
-  
+
     if Config.ExportArmatures:
         if Config.Verbose:
-            print("      Writing Mes Weights...")
-        if Config.Optimized:
-            WriteMeshSkinWeightsForGeoModel(Config, Object, Mesh, GeoModel)
-        else:
-            WriteMeshSkinWeights(Config, Object, Mesh)
+            print("      Writing Mesh Weights...")
+        WriteMeshSkinWeightsForGeoModel(Config, Object, Mesh, GeoModel)
         if Config.Verbose:
             print("      Done")
 
@@ -455,10 +423,10 @@ def WriteMesh(Config, Object, Mesh,  geoFile=None, mtlFile=None, GeoModel=None):
 
 #############
 #Store one Point of a Quad or Tri in marmalade geo format: //index-list is: { <int> <int> <int> <int> <int> }   //v,vn,uv0,uv1,vc
-#############                           
+#############
 class CGeoIndexList:
     __slots__ = "v", "vn", "uv0", "uv1", "vc"
-    
+
     def __init__(self, v, vn, uv0, uv1, vc):
         self.v = v
         self.vn = vn
@@ -466,13 +434,13 @@ class CGeoIndexList:
         self.uv1 = uv1
         self.vc = vc
 
-        
+
 #############
 #Store a Quad or a Tri in marmalade geo format : 3 or 4 CIndexList depending it is a Tri or a Quad
-#############                        
+#############
 class CGeoPoly:
     __slots__ = "pointsList",
-    
+
     def __init__(self):
         self.pointsList = []
 
@@ -494,10 +462,10 @@ class CGeoPoly:
 
 #############
 #Store all the poly (tri or quad) assigned to a Material in marmalade geo format
-#############                        
+#############
 class CGeoMaterialPolys:
     __slots__ = "name", "material", "quadList", "triList", "currentPoly"
-    
+
     def __init__(self, name, material=None):
         self.name = name
         self.material = material
@@ -509,8 +477,8 @@ class CGeoMaterialPolys:
         self.currentPoly = CGeoPoly()
 
     def AddPoint(self, v, vn, uv0, uv1, vc):
-        self.currentPoly.AddPoint(v, vn, uv0, uv1, vc)       
-             
+        self.currentPoly.AddPoint(v, vn, uv0, uv1, vc)
+
     def EndPoly(self):
         if (self.currentPoly.PointsCount() == 3):
             self.triList.append(self.currentPoly)
@@ -547,12 +515,12 @@ class CGeoMaterialPolys:
 
 #############
 #Store all the information on a Model/Mesh (vertices, normal, certcies color, uv0, uv1, TRI, QUAD) in marmalade geo format
-#############  
+#############
 class CGeoModel:
     __slots__ = ("name", "MaterialsDict", "vList", "vnList", "vcList", "uv0List", "uv1List",
                 "currentMaterialPolys", "vbaseIndex","vnbaseIndex", "uv0baseIndex", "uv1baseIndex",
-                "armatureObjectName", "useBonesDict", "mapVertexGroupNames")
-                
+                "armatureObjectName", "useBonesDict", "mapVertexGroupNames", "armatureRootBone", "armatureRootBoneIndex", "skinnedVertices")
+
     def __init__(self, name):
         self.name = name
         self.MaterialsDict = {}
@@ -575,6 +543,9 @@ class CGeoModel:
         #useBonesDict[useBonesKey] = tuple(VertexGroups.group, list(Vertex))
         self.useBonesDict = {}
         self.mapVertexGroupNames = {}
+        self.armatureRootBone = None
+        self.armatureRootBoneIndex = 0
+        self.skinnedVertices = []
 
 
 
@@ -587,11 +558,11 @@ class CGeoModel:
     # add a uv coordiantes and return the current Index in the stream (index is local to the object, when we merge several object into a one Mesh)
     def AddVertexUV0(self, u, v):
         self.uv0List.append((u, v))
-        return len(self.uv0List) - 1 - self.uv0baseIndex 
+        return len(self.uv0List) - 1 - self.uv0baseIndex
 
     def AddVertexUV1(self, u, v):
         self.uv1List.append((u, v))
-        return len(self.uv1List) - 1 - self.uv1baseIndex 
+        return len(self.uv1List) - 1 - self.uv1baseIndex
 
     # add a vertexcolor if it doesn't already exist and return the current Index in the stream (index is global to all objects, when we merge several object into a one Mesh)
     def AddVertexColor(self, r, g, b, a):
@@ -619,9 +590,9 @@ class CGeoModel:
             uv0 += self.uv0baseIndex
         if uv1 != -1:
             uv1 += self.uv1baseIndex
-                
-        self.currentMaterialPolys.AddPoint(v, vn, uv0, uv1, vc)       
-                              
+
+        self.currentMaterialPolys.AddPoint(v, vn, uv0, uv1, vc)
+
     def EndPoly(self):
         self.currentMaterialPolys.EndPoly()
         self.MaterialsDict[self.currentMaterialPolys.name] = self.currentMaterialPolys
@@ -651,6 +622,9 @@ class CGeoModel:
         self.useBonesDict = {}
         self.mapVertexGroupNames = {}
         self.armatureObjectName = ""
+        self.armatureRootBone = None
+        self.armatureRootBoneIndex = 0
+        self.skinnedVertices = []
 
     def PrintGeoMesh(self, geoFile):
         geoFile.write("\tCMesh\n")
@@ -662,7 +636,7 @@ class CGeoModel:
             geoFile.write("\t\t{\n")
             geoFile.write("\t\t\tnumVerts %d\n" % len(self.vList))
             for vertex in self.vList:
-                geoFile.write("\t\t\tv { %.9f, %.9f, %.9f }\n" % (vertex[0], vertex[1], vertex[2]))                      
+                geoFile.write("\t\t\tv { %.9f, %.9f, %.9f }\n" % (vertex[0], vertex[1], vertex[2]))
             geoFile.write("\t\t}\n")
 
         if self.vnList:
@@ -670,7 +644,7 @@ class CGeoModel:
             geoFile.write("\t\t{\n")
             geoFile.write("\t\t\tnumVertNorms  %d\n" % len(self.vnList))
             for vertexn in self.vnList:
-                geoFile.write("\t\t\tvn { %.9f, %.9f, %.9f }\n" % (vertexn[0], vertexn[1], vertexn[2]))                      
+                geoFile.write("\t\t\tvn { %.9f, %.9f, %.9f }\n" % (vertexn[0], vertexn[1], vertexn[2]))
             geoFile.write("\t\t}\n")
 
         if self.vcList:
@@ -678,7 +652,7 @@ class CGeoModel:
             geoFile.write("\t\t{\n")
             geoFile.write("\t\t\tnumVertCols %d\n" % len(self.vcList))
             for color in self.vcList:
-                geoFile.write("\t\t\tcol { %.6f, %.6f, %.6f, %.6f }\n" % (color[0], color[1], color[2], color[3])) #alpha is not supported on blender for vertex colors           
+                geoFile.write("\t\t\tcol { %.6f, %.6f, %.6f, %.6f }\n" % (color[0], color[1], color[2], color[3])) #alpha is not supported on blender for vertex colors
             geoFile.write("\t\t}\n")
 
         if self.uv0List:
@@ -687,7 +661,7 @@ class CGeoModel:
             geoFile.write("\t\t\tsetID 0\n")
             geoFile.write("\t\t\tnumUVs %d\n" % len(self.uv0List))
             for uv in self.uv0List:
-                 geoFile.write("\t\t\tuv { %.9f, %.9f }\n" % (uv[0], uv[1]))                       
+                 geoFile.write("\t\t\tuv { %.9f, %.9f }\n" % (uv[0], uv[1]))
             geoFile.write("\t\t}\n")
 
         if self.uv1List:
@@ -696,7 +670,7 @@ class CGeoModel:
             geoFile.write("\t\t\tsetID 1\n")
             geoFile.write("\t\t\tnumUVs %d\n" % len(self.uv1List))
             for uv in self.uv1List:
-                 geoFile.write("\t\t\tuv { %.9f, %.9f }\n" % (uv[0], uv[1]))                       
+                 geoFile.write("\t\t\tuv { %.9f, %.9f }\n" % (uv[0], uv[1]))
             geoFile.write("\t\t}\n")
 
         for GeoMaterialPolys in self.MaterialsDict.values():
@@ -710,7 +684,7 @@ class CGeoModel:
         if name in self.MaterialsDict:
             return self.MaterialsDict[name].material
         else:
-            return None       
+            return None
 
 
 
@@ -720,6 +694,9 @@ def BuildOptimizedGeo(Config, Object, Mesh, GeoModel):
     if GeoModel == None:
         GeoModel = CGeoModel(filename, Object.name)
 
+    #Ensure tessfaces data are here
+    Mesh.update (calc_tessface=True)
+
     #Store Vertex stream, and Normal stream (use directly the order from blender collection
     for Vertex in Mesh.vertices:
         GeoModel.AddVertex(Vertex.co)
@@ -728,27 +705,27 @@ def BuildOptimizedGeo(Config, Object, Mesh, GeoModel):
             Normal = -Normal
         GeoModel.AddVertexNormal(Normal)
     #Check if some colors have been defined
-    vertexColours = None
+    vertexColors = None
     if Config.ExportVertexColors and (len(Mesh.vertex_colors) > 0):
-        vertexColours = Mesh.vertex_colors[0].data
+        vertexColors = Mesh.tessface_vertex_colors[0].data
 
     #Check if some uv coordinates have been defined
     UVCoordinates = None
     if Config.ExportTextures and (len(Mesh.uv_textures) > 0):
-        for UV in Mesh.uv_textures:
+        for UV in Mesh.tessface_uv_textures:
             if UV.active_render:
                 UVCoordinates = UV.data
                 break
 
     #Iterate on Faces and Store the poly (quad or tri) and the associate colors,UVs
-    for Face in Mesh.faces:
+    for Face in Mesh.tessfaces:
         # stream for vertex (we use the same for normal)
         Vertices = list(Face.vertices)
         if Config.CoordinateSystem == 1:
             Vertices = Vertices[::-1]
         # stream for vertex colors
-        if vertexColours:
-            MeshColor = vertexColours[Face.index]
+        if vertexColors:
+            MeshColor = vertexColors[Face.index]
             if len(Vertices) == 3:
                 FaceColors = list((MeshColor.color1, MeshColor.color2, MeshColor.color3))
             else:
@@ -772,7 +749,7 @@ def BuildOptimizedGeo(Config, Object, Mesh, GeoModel):
                 uvVertices = uvVertices[::-1]
             uv0Index = []
             for uvVertex in uvVertices:
-                index = GeoModel.AddVertexUV0(uvVertex[0], 1 - uvVertex[1]) 
+                index = GeoModel.AddVertexUV0(uvVertex[0], 1 - uvVertex[1])
                 uv0Index.append(index)
         else:
             uv0Index = list((-1, -1, -1, -1))
@@ -787,8 +764,8 @@ def BuildOptimizedGeo(Config, Object, Mesh, GeoModel):
         if mat:
             matName =  mat.name
         else:
-            matName = "NoMaterialAssigned"  # There is no material assigned in blender !!!, exporter have generated a default one          
-            
+            matName = "NoMaterialAssigned"  # There is no material assigned in blender !!!, exporter have generated a default one
+
         # now on the material, generates the tri/quad in v,vn,uv0,uv1,vc stream index
         GeoModel.BeginPoly(matName, mat)
 
@@ -797,7 +774,8 @@ def BuildOptimizedGeo(Config, Object, Mesh, GeoModel):
 
         GeoModel.EndPoly()
 
-                              
+
+
 #############
 # Get the list of Material in use by the CGeoModel
 def WriteMeshMaterialsForGeoModel(Config, mtlFile, GeoModel):
@@ -806,284 +784,6 @@ def WriteMeshMaterialsForGeoModel(Config, mtlFile, GeoModel):
         WriteMaterial(Config, mtlFile, Material)
 
 
-##################### Not Optimized Export, we use a Face export
-
-
-def WriteMeshVerticesAndNormals(Config,  Object, Mesh, geoFile):
-    # Not optimized, simply iterate Blender Face, and writes all face vertices
-    # Marmalade groups per material, and then groups per Tir and Quad.
-    # So generate vertices grouped together per Tri of the same material, and same quad of the same material
-    geoFile.write("\tname \"%s\"\n" % (StripName(Object.name)))
-    geoFile.write("\tCMesh\n")
-    geoFile.write("\t{\n")
-    geoFile.write("\t\tname \"%s\"\n" % (StripName(Object.name)))
-    geoFile.write("\t\tCVerts\n")
-    geoFile.write("\t\t{\n")
-    Index = 0
-    VertexCount = GetNonOptimizedMeshVertexCount(Mesh)
-    geoFile.write("\t\t\tnumVerts %d\n" % VertexCount)
-                
-    if Config.Verbose:
-            print("      Writing Mesh vertices...%d => %d" % (len(Mesh.vertices), VertexCount))
-    matCount =  len(Mesh.materials)
-    if matCount == 0:
-        matCount = 1  #No material defined for the Mesh !!!! => generate a default Material
-    for matIndex in range(0, matCount):
-        if Config.Verbose:
-            print("      Material Index: %d >" % matIndex)
-        for polyCount in range(3, 5):
-            faceCount = 0
-            for Face in Mesh.faces:
-                if Face.material_index == matIndex:
-                    if len(Face.vertices) == polyCount:
-                        Vertices = list(Face.vertices)
-                        faceCount = faceCount + 1                           
-                        if Config.CoordinateSystem == 1:
-                            Vertices = Vertices[::-1]
-                        for Vertex in [Mesh.vertices[Vertex] for Vertex in Vertices]:
-                            Position = Vertex.co
-                            geoFile.write("\t\t\tv { %.9f, %.9f, %.9f }\n" % (Position[0], Position[1], Position[2]))            
-            if Config.Verbose and polyCount == 3:
-                print("         Tri Poly count  Index: %d" % faceCount)
-            elif Config.Verbose and polyCount == 4:
-                print("         Quad Poly count  Index: %d" % faceCount)
-    geoFile.write("\t\t}\n")
-
-    #WriteMeshNormals
-    geoFile.write("\t\tCVertNorms\n")
-    geoFile.write("\t\t{\n")
-    geoFile.write("\t\t\tnumVertNorms  %d\n" % VertexCount)
-      
-    if Config.Verbose:
-            print("      Writing Mesh normals...")
-    matCount =  len(Mesh.materials)
-    if matCount == 0:
-        matCount = 1  # No material defined for the Mesh !!!! => generate a default Material
-    for matIndex in range(0, matCount):
-        if Config.Verbose:
-            print("      Material Index: %d >" % matIndex)
-        for polyCount in range(3, 5):
-            faceCount = 0
-            for Face in Mesh.faces:
-                 if Face.material_index == matIndex:
-                    if len(Face.vertices) == polyCount:
-                        Vertices = list(Face.vertices)
-                        faceCount = faceCount + 1
-                        if Config.CoordinateSystem == 1:
-                            Vertices = Vertices[::-1]
-                        for Vertex in [Mesh.vertices[Vertex] for Vertex in Vertices]:
-                            if Face.use_smooth:
-                                Normal = Vertex.normal
-                            else:
-                                Normal = Face.normal
-                            if Config.FlipNormals:
-                                Normal = -Normal
-                            geoFile.write("\t\t\tvn { %.9f, %.9f, %.9f }\n" % (Normal[0], Normal[1], Normal[2]))            
-            if Config.Verbose and polyCount == 3:
-                print("         Tri Poly count  Index: %d" % faceCount)
-            elif Config.Verbose and polyCount == 4:
-                print("         Quad Poly count  Index: %d" % faceCount)
-    geoFile.write("\t\t}\n")
-
-
-def WriteMeshVerticesColors (Config, Mesh, geoFile):
-    if len(Mesh.vertex_colors) > 0:
-        vertexColours = Mesh.vertex_colors[0].data
-        if len(vertexColours) > 0:
-            geoFile.write("\t\tCVertCols\n")
-            geoFile.write("\t\t{\n")
-            Index = 0
-            VertexCount = GetNonOptimizedMeshVertexCount(Mesh)
-            geoFile.write("\t\t\tnumVertCols %d\n" % VertexCount)
-                                
-            if Config.Verbose:
-                    print("      Writing Mesh vertices Colors...%d" % (len(Mesh.vertices)))
-                    
-            matCount =  len(Mesh.materials)
-            if matCount == 0:
-                matCount = 1  #No material defined for the Mesh !!!! => generate a default Material
-            for matIndex in range(0, matCount):
-                if Config.Verbose:
-                    print("      Material Index: %d >" % matIndex)
-                for polyCount in range(3, 5):
-                    faceCount = 0
-                    for Face in Mesh.faces:
-                        if Face.material_index == matIndex:
-                            if len(Face.vertices) == polyCount:
-                                Vertices = list(Face.vertices)
-                                print("       - Face Index: %d / %d" % (Face.index, len(vertexColours)))
-                                print(vertexColours)
-                                MeshColor = vertexColours[Face.index]
-                                if polyCount == 3:
-                                    FaceColors = list((MeshColor.color1, MeshColor.color2, MeshColor.color3))
-                                else:
-                                    FaceColors = list((MeshColor.color1, MeshColor.color2, MeshColor.color3, MeshColor.color4))
-                                faceCount = faceCount + 1                           
-                                if Config.CoordinateSystem == 1:
-                                    Vertices = Vertices[::-1]
-                                    FaceColors = FaceColors[::-1]
-                                for color in FaceColors:
-                                    geoFile.write("\t\t\tcol { %.6f, %.6f, %.6f, 1 }\n" % (color[0], color[1], color[2]))            
-                    if Config.Verbose and polyCount == 3:
-                        print("         Tri Poly count  Index: %d" % faceCount)
-                    elif Config.Verbose and polyCount == 4:
-                        print("         Quad Poly count  Index: %d" % faceCount)
-            geoFile.write("\t\t}\n")
-            return True
-    return False
-
-
-def WriteMeshUVCoordinates(Config, Mesh, geoFile):
-    geoFile.write("\t\tCUVs\n")
-    geoFile.write("\t\t{\n")
-    geoFile.write("\t\t\tsetID 0\n")
-    
-    UVCoordinates = None
-    for UV in Mesh.uv_textures:
-        if UV.active_render:
-            UVCoordinates = UV.data
-            break
-    if UVCoordinates:
-        uvCount = 0
-        VertexCount = GetNonOptimizedMeshVertexCount(Mesh)
-        geoFile.write("\t\t\tnumUVs %d\n" % VertexCount)
-
-        if Config.Verbose:
-            print("      Writing Mesh UVs...")
-        matCount =  len(Mesh.materials)
-        if matCount == 0:
-            matCount = 1  #No material defined for the Mesh !!!! => generate a default Material
-        for matIndex in range(0, matCount):
-            if Config.Verbose:
-                print("      Material Index: %d >" % matIndex)
-            for polyCount in range(3, 5):
-                faceCount = 0         
-                #for Face in UVCoordinates:
-                #for Face in Mesh.faces:
-                for i in range(0, len(Mesh.faces)):
-                    Face = Mesh.faces[i]
-                    uvFace = UVCoordinates[i]
-                    Vertices = []
-                    if Face.material_index == matIndex:
-                        if len(Face.vertices) == polyCount:
-                            for Vertex in uvFace.uv:
-                                Vertices.append(tuple(Vertex))
-                            if Config.CoordinateSystem == 1:
-                                Vertices = Vertices[::-1]
-                            for Vertex in Vertices:
-                                geoFile.write("\t\t\tuv { %.9f, %.9f }\n" % (Vertex[0], 1 - Vertex[1]))                        
-                            faceCount = faceCount + 1
-                            uvCount = uvCount + len(Vertices)
-                if Config.Verbose and polyCount == 3:
-                    print("         Tri Poly count  Index: %d" % faceCount)
-                elif Config.Verbose and polyCount == 4:
-                    print("         Quad Poly count  Index: %d" % faceCount)
-
-        geoFile.write("\t\t}\n")
-        if Config.Verbose:
-             print("         Total UVCount : %d" % uvCount)
-        return True
-    return False
-
-
-
-def WriteMeshPoly(Config, Mesh, geoFile, bVertexColors, bUVTextures):
-    # groups per tri and per Quad belonging to the same material
-    Index = 0
-    VertexCount = GetNonOptimizedMeshVertexCount(Mesh)
-
-    matCount =  len(Mesh.materials)
-    if matCount == 0:
-        matCount = 1  #No material defined for the Mesh !!!! => generate a default Material
-    for matIndex in range(0, matCount):
-        if Config.Verbose:
-            print("      Material Index: %d >" % matIndex)
-            
-        #first check if there is Tri, Quad , or both, or ... none :-)
-        TriCount = 0
-        QuadCount = 0
-        for polyCount in range(3, 5):
-            for Face in Mesh.faces:
-                if Face.material_index == matIndex:
-                    if len(Face.vertices) == polyCount:
-                        if polyCount == 3:
-                            TriCount = TriCount + 1
-                        elif polyCount == 4:
-                            QuadCount = QuadCount + 1
-                            
-        if Config.Verbose:
-            print("            Poly Count Tris %d - Quads %d " % (TriCount, QuadCount))
-            
-        if TriCount > 0 or QuadCount > 0:
-            geoFile.write("\t\tCSurface\n")
-            geoFile.write("\t\t{\n")
-            if matIndex < len(Mesh.materials):
-                geoFile.write("\t\t\tmaterial \"%s\"\n" % Mesh.materials[matIndex].name)
-            else:
-                geoFile.write("\t\t\tmaterial NoMaterialAssigned // There is no material assigned in blender !!!, exporter have generated a default one\n")
-            streamIndex = 0
-            #Write the Tri for this material, if any
-            if TriCount > 0:
-                geoFile.write("\t\t\tCTris\n")
-                geoFile.write("\t\t\t{\n")
-                geoFile.write("\t\t\t\tnumTris %d\n" % (TriCount))
-                for Face in Mesh.faces:
-                    if Face.material_index == matIndex:
-                        if len(Face.vertices) == 3:
-                            vc1 = vc2 = vc3 = -1
-                            if bVertexColors:
-                                vc1 = streamIndex
-                                vc2 = streamIndex + 1
-                                vc3 = streamIndex + 2
-                            uv1 = uv2 = uv3 = -1
-                            if bUVTextures:
-                                uv1 = streamIndex
-                                uv2 = streamIndex+1
-                                uv3 = streamIndex+2
-                            geoFile.write("\t\t\t\tt {%d, %d, %d, -1, %d} {%d, %d, %d, -1, %d} {%d, %d, %d, -1, %d}\n"
-                                          % (streamIndex, streamIndex, uv1, vc1, streamIndex+1, streamIndex+1, uv2, vc2, streamIndex+2, streamIndex+2, uv3, vc3))
-                            streamIndex = streamIndex + 3
-                geoFile.write("\t\t\t}\n")
-            #Write the Quad for this material, if any
-            if QuadCount > 0:
-                geoFile.write("\t\t\tCQuads\n")
-                geoFile.write("\t\t\t{\n")
-                geoFile.write("\t\t\t\tnumQuads %d\n" % (QuadCount))
-                for Face in Mesh.faces:
-                    if Face.material_index == matIndex:
-                        if len(Face.vertices) == 4:
-                            vc1 = vc2 = vc3 = vc4 = -1
-                            if bVertexColors:
-                                vc1 = streamIndex
-                                vc2 = streamIndex + 1
-                                vc3 = streamIndex + 2
-                                vc3 = streamIndex + 3
-                            uv1 = uv2 = uv3 = uv4 = -1
-                            if bUVTextures:
-                                uv1 = streamIndex
-                                uv2 = streamIndex + 1
-                                uv3 = streamIndex + 2
-                                uv4 = streamIndex + 3
-                            geoFile.write("\t\t\t\tq {%d, %d, %d, -1, %d} {%d, %d, %d, -1, %d} {%d, %d, %d, -1, %d} {%d, %d, %d, -1, %d}\n"
-                                          % (streamIndex, streamIndex, uv1, vc1, streamIndex+1, streamIndex+1, uv2, vc2,
-                                             streamIndex+2, streamIndex+2, uv3, vc3, streamIndex+3, streamIndex+3, uv4, vc4))
-                            streamIndex = streamIndex + 4
-                geoFile.write("\t\t\t}\n")
-
-            geoFile.write("\t\t}\n")
-
-
-def WriteMeshMaterials(Config, Mesh, mtlFile):    
-    Materials = Mesh.materials
-    if Materials.keys():
-        for Material in Materials:
-            WriteMaterial(Config, mtlFile, Material)
-    else:
-        if Config.Verbose :
-            print("         NO MATERIAL ASSIGNED TO THE MESH in Blender !!! generating a default material")
-        WriteMaterial(Config, mtlFile)         
-
-
 def WriteMaterial(Config, mtlFile, Material=None):
     mtlFile.write("CIwMaterial\n")
     mtlFile.write("{\n")
@@ -1094,15 +794,15 @@ def WriteMaterial(Config, mtlFile, Material=None):
             #if bpy.context.scene.world:
             #    MatAmbientColor = Material.ambient * bpy.context.scene.world.ambient_color
             MatAmbientColor = Material.ambient * Material.diffuse_color
-            mtlFile.write("\tcolAmbient {%.2f,%.2f,%.2f,%.2f} \n" % (MatAmbientColor[0] * 255, MatAmbientColor[1] * 255, MatAmbientColor[2] * 255, Material.alpha * 255))
-            MatDiffuseColor = Material.diffuse_intensity * Material.diffuse_color
-            mtlFile.write("\tcolDiffuse  {%.2f,%.2f,%.2f} \n" % (MatDiffuseColor * 255)[:])
-            MatSpecularColor = Material.specular_intensity * Material.specular_color
-            mtlFile.write("\tcolSpecular  {%.2f,%.2f,%.2f} \n" % (MatSpecularColor * 255)[:])
+            mtlFile.write("\tcolAmbient {%.2f,%.2f,%.2f,%.2f} \n" % (min(255, MatAmbientColor[0] * 255), min(255, MatAmbientColor[1] * 255), min(255, MatAmbientColor[2] * 255), min(255, Material.alpha * 255)))
+            MatDiffuseColor = 255 * Material.diffuse_intensity * Material.diffuse_color
+            MatDiffuseColor = min((255, 255, 255)[:],MatDiffuseColor[:])
+            mtlFile.write("\tcolDiffuse  {%.2f,%.2f,%.2f} \n" % (MatDiffuseColor[:]))
+            MatSpecularColor = 255 * Material.specular_intensity * Material.specular_color
+            MatSpecularColor = min((255, 255, 255)[:],MatSpecularColor[:])
+            mtlFile.write("\tcolSpecular  {%.2f,%.2f,%.2f} \n" % (MatSpecularColor[:]))
             # EmitColor = Material.emit * Material.diffuse_color
             # mtlFile.write("\tcolEmissive {%.2f,%.2f,%.2f} \n" % (EmitColor* 255)[:])
-
-            
     else:
         mtlFile.write("\tname \"NoMaterialAssigned\" // There is no material assigned in blender !!!, exporter have generated a default one\n")
 
@@ -1111,13 +811,13 @@ def WriteMaterial(Config, mtlFile, Material=None):
         Texture = GetMaterialTextureFullPath(Config, Material)
         if Texture:
             mtlFile.write("\ttexture0 .\\textures\\%s\n" % (bpy.path.basename(Texture)))
-            
+
             if Config.CopyTextureFiles:
                 if not os.path.exists(Texture):
                     #try relative path to the blend file
                     Texture = os.path.dirname(bpy.data.filepath) + Texture
                 if os.path.exists(Texture):
-                    textureDest = os.path.dirname(Config.FilePath) + "\\models\\textures\\%s" % (bpy.path.basename(Texture))
+                    textureDest = os.path.dirname(Config.FilePath) + os.sep + "models" + os.sep + "textures" + os.sep + ("%s" % bpy.path.basename(Texture))
                     ensure_dir(textureDest)
                     if Config.Verbose:
                         print("      Copying the texture file %s ---> %s" % (Texture, textureDest))
@@ -1127,74 +827,76 @@ def WriteMaterial(Config, mtlFile, Material=None):
                         print("      CANNOT Copy texture file (not found) %s" % (Texture))
     mtlFile.write("}\n")
 
+def GetFirstRootBone(ArmatureObject):
+    ArmatureBones = ArmatureObject.data.bones
+    ParentBoneList = [Bone for Bone in ArmatureBones if Bone.parent is None]
+    if ParentBoneList:
+        return ParentBoneList[0]
+    return None
 
-def WriteMeshSkinWeights(Config, Object, Mesh):
-    ArmatureList = [Modifier for Modifier in Object.modifiers if Modifier.type == "ARMATURE"]
-    if ArmatureList:
-        ArmatureObject = ArmatureList[0].object
-        if ArmatureObject is None:
-            return
-        ArmatureBones = ArmatureObject.data.bones
 
-        # Marmlade need to declare a vertex per list of affected bones
-        # so first we have to get all the combinations of affected bones that exist int he mesh
-        # to build thoses groups, we build a unique key (like a bit field, where each bit is a VertexGroup.Index): Sum(2^VertGroupIndex)... so we have a unique Number per combinations
+def GetVertexGroupFromBone(Object, Bone):
+    if Bone:
+        vertexGroupList = [VertexGroup for VertexGroup in Object.vertex_groups  if VertexGroup.name == Bone.name]
+        if vertexGroupList:
+            return vertexGroupList[0]
+    return None
+
+
+def GetBoneListNames(Bones):
+    boneList = []
+    for Bone in Bones:
+        boneList.append(Bone.name)
+        boneList += GetBoneListNames(Bone.children)
+    return boneList
+
+
+def FindUniqueIndexForRootBone(Object, RootVertexGroup):
+    if RootVertexGroup:
+        return RootVertexGroup.index
+    else:
+        #If there is not VertexGroup associated to the root bone name, we don't have a vertex index.
+        #so use the next available free index
+        return len(Object.vertex_groups)
 
-        useBonesDict = {}
-        #useBonesKey => pair_ListGroupIndices_ListAssignedVertices
-        #useBonesDict[useBonesKey] = tuple(VertexGroups.group, list(Vertex))
 
-        mapVertexGroupNames = {} 
-        matCount = len(Mesh.materials)
-        if matCount == 0:
-            matCount = 1 #No material defined for the Mesh !!!! => generate a default Material
-        for matIndex in range(0, matCount):
-            streamIndex = 0
-            for polyCount in range(3, 5):
-                for Face in Mesh.faces:
-                    if Face.material_index == matIndex:
-                        if len(Face.vertices) == polyCount:
-                            Vertices = list(Face.vertices)
-                            if Config.CoordinateSystem == 1:
-                                Vertices = Vertices[::-1]
-                            for Vertex in [Mesh.vertices[Vertex] for Vertex in Vertices]:
-                                AddVertexToDicionarySkinWeights(Config, Object, Mesh, Vertex, useBonesDict, mapVertexGroupNames, streamIndex) 
-                                streamIndex = streamIndex + 1
-
-        PrintSkinWeights(Config, StripName(ArmatureObject.name), useBonesDict, mapVertexGroupNames, StripName(Object.name))
-
-         
 def WriteMeshSkinWeightsForGeoModel(Config, Object, Mesh, GeoModel):
     ArmatureList = [Modifier for Modifier in Object.modifiers if Modifier.type == "ARMATURE"]
     if ArmatureList:
         ArmatureObject = ArmatureList[0].object
         if ArmatureObject is None:
             return
-        ArmatureBones = ArmatureObject.data.bones
+        RootBone = GetFirstRootBone(ArmatureObject)
+        RootVertexGroup = GetVertexGroupFromBone(Object, RootBone)
+        BoneNames = GetBoneListNames(ArmatureObject.data.bones)
 
         GeoModel.armatureObjectName = StripName(ArmatureObject.name)
+        if RootBone:
+            GeoModel.armatureRootBone = RootBone
+            GeoModel.armatureRootBoneIndex = FindUniqueIndexForRootBone(Object, RootVertexGroup)
 
-        # Marmlade need to declare a vertex per list of affected bones
-        # so first we have to get all the combinations of affected bones that exist inhe mesh
+        # Marmalade need to declare a vertex per list of affected bones
+        # so first we have to get all the combinations of affected bones that exist in the mesh
         # to build thoses groups, we build a unique key (like a bit field, where each bit is a VertexGroup.Index): Sum(2^VertGroupIndex)... so we have a unique Number per combinations
-        
+
         for Vertex in Mesh.vertices:
             VertexIndex = Vertex.index + GeoModel.vbaseIndex
-            AddVertexToDicionarySkinWeights(Config, Object, Mesh, Vertex, GeoModel.useBonesDict, GeoModel.mapVertexGroupNames, VertexIndex)
+            AddVertexToDicionarySkinWeights(Config, Object, Mesh, Vertex, GeoModel.useBonesDict, GeoModel.mapVertexGroupNames, VertexIndex, RootBone, RootVertexGroup, BoneNames)
+            GeoModel.skinnedVertices.append(VertexIndex)
 
         if Config.MergeModes != 1:
             # write skin file directly
             PrintSkinWeights(Config, GeoModel.armatureObjectName, GeoModel.useBonesDict, GeoModel.mapVertexGroupNames, StripName(Object.name))
 
 
-def PrintSkinWeights(Config, ArmatureObjectName, useBonesDict, mapVertexGroupNames, GeoName):        
+def PrintSkinWeights(Config, ArmatureObjectName, useBonesDict, mapVertexGroupNames, GeoName):
         #Create the skin file
-        skinfullname = os.path.dirname(Config.FilePath) + "\models\%s.skin" % GeoName
+        skinfullname = os.path.dirname(Config.FilePath) + os.sep + "models" + os.sep + "%s.skin" % GeoName
         ensure_dir(skinfullname)
         if Config.Verbose:
             print("      Creating skin file %s" % (skinfullname))
         skinFile = open(skinfullname, "w")
-        skinFile.write('// skin file exported from : %r\n' % os.path.basename(bpy.data.filepath))   
+        skinFile.write('// skin file exported from : %r\n' % os.path.basename(bpy.data.filepath))
         skinFile.write("CIwAnimSkin\n")
         skinFile.write("{\n")
         skinFile.write("\tskeleton \"%s\"\n" % ArmatureObjectName)
@@ -1219,7 +921,7 @@ def PrintSkinWeights(Config, ArmatureObjectName, useBonesDict, mapVertexGroupNam
         skinFile.close()
 
 
-def AddVertexToDicionarySkinWeights(Config, Object, Mesh, Vertex, useBonesDict, mapVertexGroupNames, VertexIndex):
+def AddVertexToDicionarySkinWeights(Config, Object, Mesh, Vertex, useBonesDict, mapVertexGroupNames, VertexIndex, RootBone, RootVertexGroup, BoneNames):
     #build useBones
     useBonesKey = 0
     vertexGroupIndices = []
@@ -1227,21 +929,31 @@ def AddVertexToDicionarySkinWeights(Config, Object, Mesh, Vertex, useBonesDict,
     if (len(Vertex.groups)) > 4:
         print ("ERROR Vertex %d is influenced by more than 4 bones\n" % (VertexIndex))
     for VertexGroup in Vertex.groups:
-        mapVertexGroupNames[VertexGroup.group] = StripBoneName(Object.vertex_groups[VertexGroup.group].name)
-        if (len(vertexGroupIndices))<4:  #ignore if more 4 bones are influencing the vertex
-            useBonesKey = useBonesKey + pow(2, VertexGroup.group)
-            vertexGroupIndices.append(VertexGroup.group)
-            weightTotal = weightTotal + VertexGroup.weight
+        if (VertexGroup.weight > 0):
+            groupName = Object.vertex_groups[VertexGroup.group].name
+            if groupName in BoneNames:
+                mapVertexGroupNames[VertexGroup.group] = StripBoneName(groupName)
+                if (len(vertexGroupIndices))<4:  #ignore if more 4 bones are influencing the vertex
+                    useBonesKey = useBonesKey + pow(2, VertexGroup.group)
+                    vertexGroupIndices.append(VertexGroup.group)
+                    weightTotal = weightTotal + VertexGroup.weight
     if (weightTotal == 0):
-        print(" ERROR Weight is ZERO for vertex %d " % (VertexIndex))
-        print(vertexGroupIndices)
-        bWeightTotZero = True  #avoid divide by zero
+        bWeightTotZero = True  #avoid divide by zero later on
+        if (RootBone):
+            if Config.Verbose:
+                print(" Warning Weight is ZERO for vertex %d => Add it to the root bone" % (VertexIndex))
+            RootBoneGroupIndex = FindUniqueIndexForRootBone(Object, RootVertexGroup)
+            mapVertexGroupNames[RootBoneGroupIndex] = StripBoneName(RootBone.name)
+            useBonesKey = pow(2, RootBoneGroupIndex)
+            vertexGroupIndices = list((RootBoneGroupIndex,))
+
+            weightTotal = 1
     else:
         bWeightTotZero = False
-    
+
     if len(vertexGroupIndices) > 0:
         vertexGroupIndices.sort();
-           
+
         #build the vertex weight string: vertex indices, followed by influence weight for each bone
         VertexWeightString = "\t\tvertWeights { %d" % (VertexIndex)
         for vertexGroupIndex in vertexGroupIndices:
@@ -1257,11 +969,11 @@ def AddVertexToDicionarySkinWeights(Config, Object, Mesh, Vertex, useBonesDict,
                 VertexWeightString += ", %.7f" % (1.0 / len(vertexGroupIndices))
         VertexWeightString += "}"
         if bWeightTotZero:
-            VertexWeightString += " // total weight was zero in blender , export assign default weighting." 
+            VertexWeightString += " // total weight was zero in blender , export assign it to the RootBone with weight 1."
         if (len(Vertex.groups)) > 4:
             VertexWeightString += " // vertex is associated to more than 4 bones in blender !! skip some bone association (was associated to %d bones)." % (len(Vertex.groups))
         VertexWeightString += "\n"
-           
+
         #store in dictionnary information
         if useBonesKey not in useBonesDict:
             VertexList = []
@@ -1272,19 +984,19 @@ def AddVertexToDicionarySkinWeights(Config, Object, Mesh, Vertex, useBonesDict,
             pair_ListGroupIndices_ListAssignedVertices[1].append(VertexWeightString)
             useBonesDict[useBonesKey] = pair_ListGroupIndices_ListAssignedVertices
     else:
-        print ("ERROR Vertex %d is not skinned (it doesn't belong to any vertex group\n" % (VertexIndex)) 
+        print ("ERROR Vertex %d is not skinned (it doesn't belong to any vertex group\n" % (VertexIndex))
 
 
 
-############# ARMATURE: Bone export, and Bone animation export 
+############# ARMATURE: Bone export, and Bone animation export
+
 
-         
 def WriteArmatureParentRootBones(Config, Object, RootBonesList, skelFile):
 
     if len(RootBonesList) > 1:
         print(" /!\\  WARNING ,Marmelade need only one ROOT bone per armature, there is %d root bones " % len(RootBonesList))
         print(RootBonesList)
-        
+
     PoseBones = Object.pose.bones
     for Bone in RootBonesList:
         if Config.Verbose:
@@ -1292,12 +1004,11 @@ def WriteArmatureParentRootBones(Config, Object, RootBonesList, skelFile):
 
         PoseBone = PoseBones[Bone.name]
         WriteBonePosition(Config, Object, Bone, PoseBones, PoseBone, skelFile, True)
-        #WriteOneBoneRestPosition(Config, Object, Bone, PoseBones, PoseBone, skelFile, True, Vector(),Quaternion())
         if Config.Verbose:
             print("      Done")
         WriteArmatureChildBones(Config, Object, Bone.children, skelFile)
 
-            
+
 def WriteArmatureChildBones(Config, Object, BonesList, skelFile):
     PoseBones = Object.pose.bones
     for Bone in BonesList:
@@ -1305,79 +1016,92 @@ def WriteArmatureChildBones(Config, Object, BonesList, skelFile):
             print("      Writing Child Bone: {}...".format(Bone.name))
         PoseBone = PoseBones[Bone.name]
         WriteBonePosition(Config, Object, Bone, PoseBones, PoseBone, skelFile, True)
-        #WriteOneBoneRestPosition(Config, Object, Bone, PoseBones, PoseBone, skelFile, True, Vector(),Quaternion())
         if Config.Verbose:
             print("      Done")
-            
+
         WriteArmatureChildBones(Config, Object, Bone.children, skelFile)
 
 
-def WriteBonePosition(Config, Object, Bone, PoseBones, PoseBone, File, isSkelFileNotAnimFile):
-    # Compute armature scale : 
+def WriteBonePosition(Config, Object, Bone, PoseBones, PoseBone, File, isRestPoseNotAnimPose):
+    # Compute armature scale :
     # Many others exporter require sthe user to do Apply Scale in Object Mode to have 1,1,1 scale and so that anim data are correctly scaled
     # Here we retreive the Scale of the Armture Object.matrix_world.to_scale() and we use it to scale the bones :-)
     # So new Blender user should not complain about bad animation export if they forgot to apply the Scale to 1,1,1
 
     armScale = Object.matrix_world.to_scale()
-    ## scalematrix = Matrix()
-    ## scalematrix[0][0] = armScale.x * Config.Scale
-    ## scalematrix[1][1] = armScale.y * Config.Scale
-    ## scalematrix[2][2] = armScale.z * Config.Scale
-
-    if isSkelFileNotAnimFile:
+    armRot = Object.matrix_world.to_quaternion()
+    if isRestPoseNotAnimPose:
         #skel file, bone header
         File.write("\tCIwAnimBone\n")
         File.write("\t{\n")
         File.write("\t\tname \"%s\"\n" % StripBoneName(Bone.name))
+        #get bone local matrix for rest pose
         if Bone.parent:
             File.write("\t\tparent \"%s\"\n" % StripBoneName(Bone.parent.name))
+            localmat = Bone.parent.matrix_local.inverted() * Bone.matrix_local
+        else:
+            localmat = Bone.matrix_local
     else:
         #anim file, bone header
         File.write("\t\t\n")
         File.write("\t\tbone \"%s\" \n" % StripBoneName(Bone.name))
+        localmat = PoseBone.matrix
+        #get bone local matrix for current anim pose
+        if Bone.parent:
+            ParentPoseBone = PoseBones[Bone.parent.name]
+            localmat = ParentPoseBone.matrix.inverted() * PoseBone.matrix
+        else:
+            localmat = PoseBone.matrix
 
-    if Bone.parent:
-        ParentPoseBone = PoseBones[Bone.parent.name]
-        locmat = ParentPoseBone.matrix.inverted() * PoseBone.matrix
-    else:
-        locmat = PoseBone.matrix
-        if Config.MergeModes > 0:
-            # Merge mode is in world coordinates .. anyway merge mesh doesn't work really with armature that should be local to one mesh
-            locmat = Object.matrix_world * PoseBone.matrix
-            armScale.x =  armScale.y = armScale.z = 1  
-        
-    loc = locmat.to_translation()
-    quat = locmat.to_quaternion()
-  
-    if not Bone.parent:  # and Config.MergeModes == 0:
-        #flip Y Z axes (only on root bone, other bones are local to root bones, so no need to rotate)
+    if not Bone.parent:
+        #Flip Y Z axes (only on root bone, other bones are local to root bones, so no need to rotate)
         X_ROT = mathutils.Matrix.Rotation(-math.pi / 2, 4, 'X')
-        quat.rotate(X_ROT)
-        loc.rotate(X_ROT)
+        if Config.MergeModes > 0:
+            # Merge mode is in world coordinates and not in model coordinates: so apply the world coordinate on the rootbone
+            localmat = X_ROT * Object.matrix_world * localmat
+            armScale.x =  armScale.y = armScale.z = 1
+        else:
+            localmat= X_ROT * armRot.to_matrix().to_4x4() * localmat #apply the armature rotation on the root bone
+
+
+    loc = localmat.to_translation()
+    quat = localmat.to_quaternion()
 
-        
     #Scale the bone
     loc.x *= (armScale.x * Config.Scale)
     loc.y *= (armScale.y * Config.Scale)
     loc.z *= (armScale.z * Config.Scale)
-    
+
     File.write("\t\tpos { %.9f, %.9f, %.9f }\n" % (loc[0], loc[1], loc[2]))
     File.write("\t\trot { %.9f, %.9f, %.9f, %.9f }\n" % (quat.w, quat.x, quat.y, quat.z))
 
-    if isSkelFileNotAnimFile:
+    if isRestPoseNotAnimPose:
         File.write("\t}\n")
 
-      
-def WriteKeyedAnimationSet(Config, Scene):  
+
+def WriteKeyedAnimationSet(Config, Scene):
     for Object in [Object for Object in Config.ObjectList if Object.animation_data]:
         if Config.Verbose:
             print("  Writing Animation Data for Object: {}".format(Object.name))
-        Action = Object.animation_data.action
-        if Action:
+        actions = []
+        if Config.ExportAnimationActions == 0 and Object.animation_data.action:
+            actions.append(Object.animation_data.action)
+        else:
+            actions = bpy.data.actions[:]
+            DefaultAction = Object.animation_data.action
+
+        for Action in actions:
+            if Config.ExportAnimationActions == 0:
+                animFileName = StripName(Object.name)
+            else:
+                Object.animation_data.action = Action
+                animFileName = "%s_%s" % (StripName(Object.name),StripName(Action.name))
+
             #Object animated (aka single bone object)
             #build key frame time list
+
             keyframeTimes = set()
-            if Config.ExportAnimation == 1:
+            if Config.ExportAnimationFrames == 1:
                 # Exports only key frames
                 for FCurve in Action.fcurves:
                     for Keyframe in FCurve.keyframe_points:
@@ -1389,29 +1113,29 @@ def WriteKeyedAnimationSet(Config, Scene):
                             keyframeTimes.add(int(Keyframe.co[0]))
             else:
                 # Exports all frames
-                keyframeTimes.update(range(scene.frame_start, scene.frame_end + 1, 1))
+                keyframeTimes.update(range(Scene.frame_start, Scene.frame_end + 1, 1))
             keyframeTimes = list(keyframeTimes)
             keyframeTimes.sort()
             if len(keyframeTimes):
                 #Create the anim file for offset animation (or single bone animation
-                animfullname = os.path.dirname(Config.FilePath) + "\\anims\\%s_offset.anim" % (StripName(Object.name))
+                animfullname = os.path.dirname(Config.FilePath) + os.sep + "anims" + os.sep + "%s_offset.anim" % animFileName
                 #not yet supported
                 """
                 ##    ensure_dir(animfullname)
                 ##    if Config.Verbose:
                 ##        print("      Creating anim file (single bone animation) %s" % (animfullname))
                 ##    animFile = open(animfullname, "w")
-                ##    animFile.write('// anim file exported from : %r\n' % os.path.basename(bpy.data.filepath))   
+                ##    animFile.write('// anim file exported from : %r\n' % os.path.basename(bpy.data.filepath))
                 ##    animFile.write("CIwAnim\n")
                 ##    animFile.write("{\n")
                 ##    animFile.write("\tent \"%s\"\n" % (StripName(Object.name)))
                 ##    animFile.write("\tskeleton \"SingleBone\"\n")
                 ##    animFile.write("\t\t\n")
                 ##
-                ##    Config.File.write("\t\".\\anims\\%s_offset.anim\"\n" % (StripName(Object.name)))
+                ##    Config.File.write("\t\".\\anims\\%s_offset.anim\"\n" % animFileName))
                 ##
                 ##    for KeyframeTime in keyframeTimes:
-                ##        #Scene.frame_set(KeyframeTime)    
+                ##        #Scene.frame_set(KeyframeTime)
                 ##        animFile.write("\tCIwAnimKeyFrame\n")
                 ##        animFile.write("\t{\n")
                 ##        animFile.write("\t\ttime %.2f // frame num %d \n" % (KeyframeTime/Config.AnimFPS, KeyframeTime))
@@ -1466,10 +1190,10 @@ def WriteKeyedAnimationSet(Config, Scene):
                     print("    Writing Armature Bone Animation Data...\n")
                 PoseBones = Object.pose.bones
                 Bones = Object.data.bones
-                #riged bones animated 
+                #riged bones animated
                 #build key frame time list
                 keyframeTimes = set()
-                if Config.ExportAnimation==1:
+                if Config.ExportAnimationFrames == 1:
                     # Exports only key frames
                     for FCurve in Action.fcurves:
                         for Keyframe in FCurve.keyframe_points:
@@ -1481,25 +1205,33 @@ def WriteKeyedAnimationSet(Config, Scene):
                                 keyframeTimes.add(int(Keyframe.co[0]))
                 else:
                     # Exports all frame
-                    keyframeTimes.update(range(scene.frame_start, scene.frame_end + 1, 1))
-                   
+                    keyframeTimes.update(range(Scene.frame_start, Scene.frame_end + 1, 1))
+
                 keyframeTimes = list(keyframeTimes)
                 keyframeTimes.sort()
+                if Config.Verbose:
+                    print("Exporting frames: ")
+                    print(keyframeTimes)
+                    if (Scene.frame_preview_end > Scene.frame_end):
+                        print(" WARNING: END Frame of animation in UI preview is Higher than the Scene Frame end:\n Scene.frame_end %d versus Scene.frame_preview_end %d.\n"
+                              % (Scene.frame_end, Scene.frame_preview_end))
+                        print(" => You might need to change the Scene End Frame, to match the current UI preview frame end...\n=> if you don't want to miss end of animation.\n")
+
                 if len(keyframeTimes):
                     #Create the anim file
-                    animfullname = os.path.dirname(Config.FilePath) + "\\anims\\%s.anim" % (StripName(Object.name))
+                    animfullname = os.path.dirname(Config.FilePath) + os.sep + "anims" + os.sep + "%s.anim" % animFileName
                     ensure_dir(animfullname)
                     if Config.Verbose:
                         print("      Creating anim file (bones animation) %s\n" % (animfullname))
                         print("      Frame count %d \n" % (len(keyframeTimes)))
                     animFile = open(animfullname, "w")
-                    animFile.write('// anim file exported from : %r\n' % os.path.basename(bpy.data.filepath))   
+                    animFile.write('// anim file exported from : %r\n' % os.path.basename(bpy.data.filepath))
                     animFile.write("CIwAnim\n")
                     animFile.write("{\n")
                     animFile.write("\tskeleton \"%s\"\n" % (StripName(Object.name)))
                     animFile.write("\t\t\n")
 
-                    Config.File.write("\t\".\\anims\\%s.anim\"\n" % (StripName(Object.name)))
+                    Config.File.write("\t\".\\anims\\%s.anim\"\n" % animFileName)
 
                     for KeyframeTime in keyframeTimes:
                         if Config.Verbose:
@@ -1523,27 +1255,30 @@ def WriteKeyedAnimationSet(Config, Scene):
             else:
                 if Config.Verbose:
                     print("    Object %s has no useable animation data." % (StripName(Object.name)))
+        if Config.ExportAnimationActions == 1:
+            #set back the original default animation
+            Object.animation_data.action = DefaultAction
         if Config.Verbose:
             print("  Done") #Done with Object
 
-                                
+
+
+
 ################## Utilities
-            
+
 def StripBoneName(name):
     return name.replace(" ", "")
 
 
 def StripName(Name):
-    
+
     def ReplaceSet(String, OldSet, NewChar):
         for OldChar in OldSet:
             String = String.replace(OldChar, NewChar)
         return String
-    
+
     import string
-    
+
     NewName = ReplaceSet(Name, string.punctuation + " ", "_")
     return NewName
 
@@ -1552,7 +1287,7 @@ def ensure_dir(f):
     d = os.path.dirname(f)
     if not os.path.exists(d):
         os.makedirs(d)
-        
+
 
 def CloseFile(Config):
     if Config.Verbose:
@@ -1568,12 +1303,17 @@ CoordinateSystems = (
     )
 
 
-AnimationModes = (
+AnimationFrameModes = (
     ("0", "None", ""),
     ("1", "Keyframes Only", ""),
     ("2", "Full Animation", ""),
     )
 
+AnimationActions = (
+    ("0", "Default Animation", ""),
+    ("1", "All Animations", ""),
+    )
+
 ExportModes = (
     ("1", "All Objects", ""),
     ("2", "Selected Objects", ""),
@@ -1610,13 +1350,13 @@ class MarmaladeExporter(bpy.types.Operator):
                     "Do not merge rigged character that have an armature.",
         items=MergeModes,
         default="0")
-    
+
     #General Options
     Scale = IntProperty(
         name="Scale Percent",
         description="Scale percentage applied for export",
         default=100, min=1, max=1000)
-    
+
     FlipNormals = BoolProperty(
         name="Flip Normals",
         description="",
@@ -1645,34 +1385,32 @@ class MarmaladeExporter(bpy.types.Operator):
         name="Export Armatures",
         description="Export the bones of any armatures to deform meshes",
         default=True)
-    ExportAnimation = EnumProperty(
-        name="Animations",
+    ExportAnimationFrames = EnumProperty(
+        name="Animations Frames",
         description="Select the type of animations to export. Only object " \
-                    "and armature bone animations can be exported. Full " \
-                    "Animation exports every frame",
-        items=AnimationModes,
+                    "and armature bone animations can be exported. Keyframes exports only the keyed frames" \
+                    "Full Animation exports every frames, None disables animationq export. ",
+        items=AnimationFrameModes,
         default="1")
-    if bpy.context.scene:
-        defFPS = bpy.context.scene.render.fps
-    else:
-        defFPS = 30                 
+    ExportAnimationActions = EnumProperty(
+        name="Animations Actions",
+        description="By default only the Default Animation Action assoiated to an armature is exported." \
+                    "However if you have defined several animations on the same armature,"\
+                    "you can select to export all animations. You can see the list of animation actions in the DopeSheet window.",
+        items=AnimationActions,
+        default="0")
     AnimFPS = IntProperty(
         name="Animation FPS",
         description="Frame rate used to export animation in seconds (can be used to artficially slow down the exported animation, or to speed up it",
-        default=defFPS, min=1, max=300)
+        default=30, min=1, max=300)
 
     #Advance Options
-    Optimized = BoolProperty(
-        name="Optimized the Vertices count",
-        description="Optimize the vertices counts, uncheck if you fill that exported normals or vertex colors are not suitable",
-        default=True)
-     
     CoordinateSystem = EnumProperty(
         name="System",
         description="Select a coordinate system to export to",
         items=CoordinateSystems,
         default="1")
-    
+
     Verbose = BoolProperty(
         name="Verbose",
         description="Run the exporter in debug mode. Check the console for output",
@@ -1685,7 +1423,6 @@ class MarmaladeExporter(bpy.types.Operator):
         Config = MarmaladeExporterSettings(context,
                                          FilePath,
                                          CoordinateSystem=self.CoordinateSystem,
-                                         Optimized=self.Optimized,
                                          FlipNormals=self.FlipNormals,
                                          ApplyModifiers=self.ApplyModifiers,
                                          Scale=self.Scale,
@@ -1695,7 +1432,8 @@ class MarmaladeExporter(bpy.types.Operator):
                                          ExportTextures=self.ExportTextures,
                                          CopyTextureFiles=self.CopyTextureFiles,
                                          ExportArmatures=self.ExportArmatures,
-                                         ExportAnimation=self.ExportAnimation,
+                                         ExportAnimationFrames=self.ExportAnimationFrames,
+                                         ExportAnimationActions=self.ExportAnimationActions,
                                          ExportMode=self.ExportMode,
                                          MergeModes=self.MergeModes,
                                          Verbose=self.Verbose)
@@ -1705,14 +1443,14 @@ class MarmaladeExporter(bpy.types.Operator):
             bpy.ops.object.mode_set(mode='OBJECT')
 
         ExportMadeWithMarmaladeGroup(Config)
-        return {"FINISHED"}
+        return {'FINISHED'}
 
     def invoke(self, context, event):
         if not self.filepath:
             self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".group")
         WindowManager = context.window_manager
         WindowManager.fileselect_add(self)
-        return {"RUNNING_MODAL"}
+        return {'RUNNING_MODAL'}
 
 
 def menu_func(self, context):