Category: copy/paste UVs
[blender-addons-contrib.git] / io_export_marmalade.py
index 7353d4aa435714079480d9f401d3369ce4124daa..adf5448cfec0402ef76cd7ce93cec7aa9b2ddba0 100644 (file)
 bl_info = {
     "name": "Marmalade Cross-platform Apps (.group)",
     "author": "Benoit Muller",
-    "version": (0, 5, 4),
-    "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
@@ -64,7 +63,8 @@ class MarmaladeExporterSettings:
                  ExportTextures=True,
                  CopyTextureFiles=True,
                  ExportArmatures=False,
-                 ExportAnimation=0,
+                 ExportAnimationFrames=0,
+                 ExportAnimationActions=0,
                  ExportMode=1,
                  MergeModes=0,
                  Verbose=False):
@@ -80,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
@@ -120,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...")
@@ -139,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)
@@ -197,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)))
@@ -265,20 +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(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))
-                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:
@@ -289,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
@@ -320,7 +321,7 @@ def WriteObjects(Config, ObjectList, geoFile=None, mtlFile=None, GeoModel=None,
                             GeoModel.skinnedVertices.append(i)
                             useBonesKey = pow(2, GeoModel.armatureRootBoneIndex)
                             vertexGroupIndices = list((GeoModel.armatureRootBoneIndex,))
-                            if useBonesKey not in GeoModel.useBonesDict:                          
+                            if useBonesKey not in GeoModel.useBonesDict:
                                 GeoModel.mapVertexGroupNames[GeoModel.armatureRootBoneIndex] = StripBoneName(GeoModel.armatureRootBone.name)
                                 VertexList = []
                                 VertexList.append("\t\tvertWeights { %d, 1.0}" % i)
@@ -341,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")
@@ -354,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()
 
 
@@ -386,7 +387,7 @@ def WriteMesh(Config, Object, Mesh,  geoFile=None, mtlFile=None, GeoModel=None):
     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)
+
     if Config.Verbose:
         print("      Done\n      Writing Mesh Materials...")
 
@@ -396,7 +397,7 @@ def WriteMesh(Config, Object, Mesh,  geoFile=None, mtlFile=None, GeoModel=None):
 
     if Config.Verbose:
         print("      Done")
-  
+
     if Config.ExportArmatures:
         if Config.Verbose:
             print("      Writing Mesh Weights...")
@@ -422,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
@@ -433,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 = []
 
@@ -461,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
@@ -476,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)
@@ -514,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", "armatureRootBone", "armatureRootBoneIndex", "skinnedVertices")
-                
+
     def __init__(self, name):
         self.name = name
         self.MaterialsDict = {}
@@ -557,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):
@@ -589,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
@@ -635,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:
@@ -643,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:
@@ -651,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:
@@ -660,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:
@@ -669,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():
@@ -683,7 +684,7 @@ class CGeoModel:
         if name in self.MaterialsDict:
             return self.MaterialsDict[name].material
         else:
-            return None       
+            return None
 
 
 
@@ -693,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)
@@ -701,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:
@@ -745,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))
@@ -760,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)
 
@@ -771,7 +775,7 @@ def BuildOptimizedGeo(Config, Object, Mesh, GeoModel):
         GeoModel.EndPoly()
 
 
-                              
+
 #############
 # Get the list of Material in use by the CGeoModel
 def WriteMeshMaterialsForGeoModel(Config, mtlFile, GeoModel):
@@ -790,13 +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)[:])    
+            # 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")
 
@@ -805,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))
@@ -831,12 +837,20 @@ def GetFirstRootBone(ArmatureObject):
 
 def GetVertexGroupFromBone(Object, Bone):
     if Bone:
-        rootBoneList = [VertexGroup for VertexGroup in Object.vertex_groups  if VertexGroup.name == Bone.name]
-        if rootBoneList:
-            return rootBoneList[0]
+        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
@@ -845,7 +859,7 @@ def FindUniqueIndexForRootBone(Object, RootVertexGroup):
         #so use the next available free index
         return len(Object.vertex_groups)
 
-         
+
 def WriteMeshSkinWeightsForGeoModel(Config, Object, Mesh, GeoModel):
     ArmatureList = [Modifier for Modifier in Object.modifiers if Modifier.type == "ARMATURE"]
     if ArmatureList:
@@ -854,6 +868,7 @@ def WriteMeshSkinWeightsForGeoModel(Config, Object, Mesh, GeoModel):
             return
         RootBone = GetFirstRootBone(ArmatureObject)
         RootVertexGroup = GetVertexGroupFromBone(Object, RootBone)
+        BoneNames = GetBoneListNames(ArmatureObject.data.bones)
 
         GeoModel.armatureObjectName = StripName(ArmatureObject.name)
         if RootBone:
@@ -863,10 +878,10 @@ def WriteMeshSkinWeightsForGeoModel(Config, Object, Mesh, GeoModel):
         # 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, RootBone, RootVertexGroup)
+            AddVertexToDicionarySkinWeights(Config, Object, Mesh, Vertex, GeoModel.useBonesDict, GeoModel.mapVertexGroupNames, VertexIndex, RootBone, RootVertexGroup, BoneNames)
             GeoModel.skinnedVertices.append(VertexIndex)
 
         if Config.MergeModes != 1:
@@ -874,14 +889,14 @@ def WriteMeshSkinWeightsForGeoModel(Config, Object, Mesh, GeoModel):
             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)
@@ -906,7 +921,7 @@ def PrintSkinWeights(Config, ArmatureObjectName, useBonesDict, mapVertexGroupNam
         skinFile.close()
 
 
-def AddVertexToDicionarySkinWeights(Config, Object, Mesh, Vertex, useBonesDict, mapVertexGroupNames, VertexIndex, RootBone, RootVertexGroup):
+def AddVertexToDicionarySkinWeights(Config, Object, Mesh, Vertex, useBonesDict, mapVertexGroupNames, VertexIndex, RootBone, RootVertexGroup, BoneNames):
     #build useBones
     useBonesKey = 0
     vertexGroupIndices = []
@@ -915,11 +930,13 @@ def AddVertexToDicionarySkinWeights(Config, Object, Mesh, Vertex, useBonesDict,
         print ("ERROR Vertex %d is influenced by more than 4 bones\n" % (VertexIndex))
     for VertexGroup in Vertex.groups:
         if (VertexGroup.weight > 0):
-            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
+            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):
         bWeightTotZero = True  #avoid divide by zero later on
         if (RootBone):
@@ -933,10 +950,10 @@ def AddVertexToDicionarySkinWeights(Config, Object, Mesh, Vertex, useBonesDict,
             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:
@@ -952,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 it to the RootBone with weight 1." 
+            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 = []
@@ -967,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:
@@ -987,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:
@@ -1000,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:
@@ -1089,24 +1118,24 @@ def WriteKeyedAnimationSet(Config, Scene):
             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))
@@ -1161,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:
@@ -1177,7 +1206,7 @@ def WriteKeyedAnimationSet(Config, Scene):
                 else:
                     # Exports all frame
                     keyframeTimes.update(range(Scene.frame_start, Scene.frame_end + 1, 1))
-                   
+
                 keyframeTimes = list(keyframeTimes)
                 keyframeTimes.sort()
                 if Config.Verbose:
@@ -1190,19 +1219,19 @@ def WriteKeyedAnimationSet(Config, Scene):
 
                 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:
@@ -1226,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
 
@@ -1255,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:
@@ -1271,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", ""),
@@ -1313,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="",
@@ -1348,21 +1385,24 @@ 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
     CoordinateSystem = EnumProperty(
@@ -1370,7 +1410,7 @@ class MarmaladeExporter(bpy.types.Operator):
         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",
@@ -1392,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)
@@ -1402,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):