Advertisement
Guest User

DirectX Exporter blender (the default one)

a guest
Sep 22nd, 2012
54
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 62.57 KB | None | 0 0
  1. #  ***** GPL LICENSE BLOCK *****
  2. #
  3. #  This program is free software: you can redistribute it and/or modify
  4. #  it under the terms of the GNU General Public License as published by
  5. #  the Free Software Foundation, either version 3 of the License, or
  6. #  (at your option) any later version.
  7. #
  8. #  This program is distributed in the hope that it will be useful,
  9. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11. #  GNU General Public License for more details.
  12. #
  13. #  You should have received a copy of the GNU General Public License
  14. #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  15. #  All rights reserved.
  16. #  ***** GPL LICENSE BLOCK *****
  17.  
  18. bl_info = {
  19.     "name": "DirectX Model Format (.x)",
  20.     "author": "Chris Foster (Kira Vakaan)",
  21.     "version": (2, 1, 3),
  22.     "blender": (2, 6, 3),
  23.     "location": "File > Export > DirectX (.x)",
  24.     "description": "Export DirectX Model Format (.x)",
  25.     "warning": "",
  26.     "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"\
  27.         "Scripts/Import-Export/DirectX_Exporter",
  28.     "tracker_url": "https://projects.blender.org/tracker/index.php?"\
  29.         "func=detail&aid=22795",
  30.     "category": "Import-Export"}
  31.  
  32. import os
  33. from math import radians
  34.  
  35. import bpy
  36. from mathutils import *
  37.  
  38. #Container for the exporter settings
  39. class DirectXExporterSettings:
  40.     def __init__(self,
  41.                  context,
  42.                  FilePath,
  43.                  CoordinateSystem=1,
  44.                  RotateX=True,
  45.                  FlipNormals=False,
  46.                  ApplyModifiers=False,
  47.                  IncludeFrameRate=False,
  48.                  ExportTextures=True,
  49.                  ExportArmatures=False,
  50.                  ExportAnimation=0,
  51.                  ExportMode=1,
  52.                  Verbose=False):
  53.         self.context = context
  54.         self.FilePath = FilePath
  55.         self.CoordinateSystem = int(CoordinateSystem)
  56.         self.RotateX = RotateX
  57.         self.FlipNormals = FlipNormals
  58.         self.ApplyModifiers = ApplyModifiers
  59.         self.IncludeFrameRate = IncludeFrameRate
  60.         self.ExportTextures = ExportTextures
  61.         self.ExportArmatures = ExportArmatures
  62.         self.ExportAnimation = int(ExportAnimation)
  63.         self.ExportMode = int(ExportMode)
  64.         self.Verbose = Verbose
  65.  
  66.  
  67. def LegalName(Name):
  68.    
  69.     def ReplaceSet(String, OldSet, NewChar):
  70.         for OldChar in OldSet:
  71.             String = String.replace(OldChar, NewChar)
  72.         return String
  73.    
  74.     import string
  75.    
  76.     NewName = ReplaceSet(Name, string.punctuation + " ", "_")
  77.     if NewName[0].isdigit() or NewName in ["ARRAY",
  78.                                            "DWORD",
  79.                                            "UCHAR",
  80.                                            "BINARY",
  81.                                            "FLOAT",
  82.                                            "ULONGLONG",
  83.                                            "BINARY_RESOURCE",
  84.                                            "SDWORD",
  85.                                            "UNICODE",
  86.                                            "CHAR",
  87.                                            "STRING",
  88.                                            "WORD",
  89.                                            "CSTRING",
  90.                                            "SWORD",
  91.                                            "DOUBLE",
  92.                                            "TEMPLATE"]:
  93.         NewName = "_" + NewName
  94.     return NewName
  95.  
  96.  
  97. def ExportDirectX(Config):
  98.     print("----------\nExporting to {}".format(Config.FilePath))
  99.     if Config.Verbose:
  100.         print("Opening File...")
  101.     Config.File = open(Config.FilePath, "w")
  102.     if Config.Verbose:
  103.         print("Done")
  104.  
  105.     if Config.Verbose:
  106.         print("Generating Object list for export... (Root parents only)")
  107.     if Config.ExportMode == 1:
  108.         Config.ExportList = [Object for Object in Config.context.scene.objects
  109.                              if Object.type in {'ARMATURE', 'EMPTY', 'MESH'}
  110.                              and Object.parent is None]
  111.     else:
  112.         ExportList = [Object for Object in Config.context.selected_objects
  113.                       if Object.type in {'ARMATURE', 'EMPTY', 'MESH'}]
  114.         Config.ExportList = [Object for Object in ExportList
  115.                              if Object.parent not in ExportList]
  116.     if Config.Verbose:
  117.         print("  List: {}\nDone".format(Config.ExportList))
  118.  
  119.     if Config.Verbose:
  120.         print("Setting up...")
  121.     Config.SystemMatrix = Matrix()
  122.     if Config.RotateX:
  123.         Config.SystemMatrix *= Matrix.Rotation(radians(-90), 4, "X")
  124.     if Config.CoordinateSystem == 1:
  125.         Config.SystemMatrix *= Matrix.Scale(-1, 4, Vector((0, 1, 0)))
  126.  
  127.     if Config.ExportAnimation:
  128.         CurrentFrame = bpy.context.scene.frame_current
  129.         bpy.context.scene.frame_current = bpy.context.scene.frame_current
  130.     if Config.Verbose:
  131.         print("Done")
  132.  
  133.     if Config.Verbose:
  134.         print("Writing Header...")
  135.     WriteHeader(Config)
  136.     if Config.Verbose:
  137.         print("Done")
  138.  
  139.     Config.Whitespace = 0
  140.     if Config.Verbose:
  141.         print("Writing Root Frame...")
  142.     WriteRootFrame(Config)
  143.     if Config.Verbose:
  144.         print("Done")
  145.    
  146.     Config.ObjectList = []
  147.     if Config.Verbose:
  148.         print("Writing Objects...")
  149.     WriteObjects(Config, Config.ExportList)
  150.     if Config.Verbose:
  151.         print("Done")
  152.    
  153.     Config.Whitespace -= 1
  154.     Config.File.write("{}}} //End of Root Frame\n".format("  " * Config.Whitespace))
  155.    
  156.     if Config.Verbose:
  157.         print("Objects Exported: {}".format(Config.ExportList))
  158.  
  159.     if Config.ExportAnimation:
  160.         if Config.IncludeFrameRate:
  161.             if Config.Verbose:
  162.                 print("Writing Frame Rate...")
  163.             Config.File.write("{}AnimTicksPerSecond {{\n".format("  " * Config.Whitespace))
  164.             Config.Whitespace += 1
  165.             Config.File.write("{}{};\n".format("  " * Config.Whitespace, int(bpy.context.scene.render.fps / bpy.context.scene.render.fps_base)))
  166.             Config.Whitespace -= 1
  167.             Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  168.             if Config.Verbose:
  169.                 print("Done")
  170.         if Config.Verbose:
  171.             print("Writing Animation...")
  172.         if Config.ExportAnimation==1:
  173.             WriteKeyedAnimationSet(Config)
  174.         else:
  175.             WriteFullAnimationSet(Config)
  176.         bpy.context.scene.frame_current = CurrentFrame
  177.         if Config.Verbose:
  178.             print("Done")
  179.  
  180.     CloseFile(Config)
  181.     print("Finished")
  182.  
  183.  
  184. def GetObjectChildren(Parent):
  185.     return [Object for Object in Parent.children
  186.             if Object.type in {'ARMATURE', 'EMPTY', 'MESH'}]
  187.  
  188. #Returns the vertex count of Mesh, counting each vertex for every face.
  189. def GetMeshVertexCount(Mesh):
  190.     VertexCount = 0
  191.     for Polygon in Mesh.polygons:
  192.         VertexCount += len(Polygon.vertices)
  193.     return VertexCount
  194.  
  195. #Returns the file path of first image texture from Material.
  196. def GetMaterialTextureFileName(Material):
  197.     if Material:
  198.         #Create a list of Textures that have type "IMAGE"
  199.         ImageTextures = [Material.texture_slots[TextureSlot].texture for TextureSlot in Material.texture_slots.keys() if Material.texture_slots[TextureSlot].texture.type == "IMAGE"]
  200.         #Refine a new list with only image textures that have a file source
  201.         ImageFiles = [bpy.path.basename(Texture.image.filepath) for Texture in ImageTextures if getattr(Texture.image, "source", "") == "FILE"]
  202.         if ImageFiles:
  203.             return ImageFiles[0]
  204.     return None
  205.  
  206.  
  207. def WriteHeader(Config):
  208.     Config.File.write("xof 0303txt 0032\n\n")
  209.    
  210.     if Config.IncludeFrameRate:
  211.         Config.File.write("template AnimTicksPerSecond {\n\
  212.  <9E415A43-7BA6-4a73-8743-B73D47E88476>\n\
  213.  DWORD AnimTicksPerSecond;\n\
  214. }\n\n")
  215.  
  216.     if Config.ExportArmatures:
  217.         Config.File.write("template XSkinMeshHeader {\n\
  218.  <3cf169ce-ff7c-44ab-93c0-f78f62d172e2>\n\
  219.  WORD nMaxSkinWeightsPerVertex;\n\
  220.  WORD nMaxSkinWeightsPerFace;\n\
  221.  WORD nBones;\n\
  222. }\n\n\
  223. template SkinWeights {\n\
  224.  <6f0d123b-bad2-4167-a0d0-80224f25fabb>\n\
  225.  STRING transformNodeName;\n\
  226.  DWORD nWeights;\n\
  227.  array DWORD vertexIndices[nWeights];\n\
  228.  array float weights[nWeights];\n\
  229.  Matrix4x4 matrixOffset;\n\
  230. }\n\n")
  231.  
  232. def WriteRootFrame(Config):
  233.     Config.File.write("{}Frame Root {{\n".format("  " * Config.Whitespace))
  234.     Config.Whitespace += 1
  235.    
  236.     Config.File.write("{}FrameTransformMatrix {{\n".format("  " * Config.Whitespace))
  237.     Config.Whitespace += 1
  238.     Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format("  " * Config.Whitespace, Config.SystemMatrix[0][0], Config.SystemMatrix[1][0], Config.SystemMatrix[2][0], Config.SystemMatrix[3][0]))
  239.     Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format("  " * Config.Whitespace, Config.SystemMatrix[0][1], Config.SystemMatrix[1][1], Config.SystemMatrix[2][1], Config.SystemMatrix[3][1]))
  240.     Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format("  " * Config.Whitespace, Config.SystemMatrix[0][2], Config.SystemMatrix[1][2], Config.SystemMatrix[2][2], Config.SystemMatrix[3][2]))
  241.     Config.File.write("{}{:9f},{:9f},{:9f},{:9f};;\n".format("  " * Config.Whitespace, Config.SystemMatrix[0][3], Config.SystemMatrix[1][3], Config.SystemMatrix[2][3], Config.SystemMatrix[3][3]))
  242.     Config.Whitespace -= 1
  243.     Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  244.  
  245. def WriteObjects(Config, ObjectList):
  246.     Config.ObjectList += ObjectList
  247.  
  248.     for Object in ObjectList:
  249.         if Config.Verbose:
  250.             print("  Writing Object: {}...".format(Object.name))
  251.         Config.File.write("{}Frame {} {{\n".format("  " * Config.Whitespace, LegalName(Object.name)))
  252.  
  253.         Config.Whitespace += 1
  254.         if Config.Verbose:
  255.             print("    Writing Local Matrix...")
  256.         WriteLocalMatrix(Config, Object)
  257.         if Config.Verbose:
  258.             print("    Done")
  259.  
  260.         if Config.ExportArmatures and Object.type == "ARMATURE":
  261.             Armature = Object.data
  262.             ParentList = [Bone for Bone in Armature.bones if Bone.parent is None]
  263.             if Config.Verbose:
  264.                 print("    Writing Armature Bones...")
  265.             WriteArmatureBones(Config, Object, ParentList)
  266.             if Config.Verbose:
  267.                 print("    Done")
  268.        
  269.         ChildList = GetObjectChildren(Object)
  270.         if Config.ExportMode == 2: #Selected Objects Only
  271.             ChildList = [Child for Child in ChildList
  272.                          if Child in Config.context.selected_objects]
  273.         if Config.Verbose:
  274.             print("    Writing Children...")
  275.         WriteObjects(Config, ChildList)
  276.         if Config.Verbose:
  277.             print("    Done Writing Children")
  278.  
  279.         if Object.type == "MESH":
  280.             if Config.Verbose:
  281.                 print("    Generating Mesh...")
  282.             if Config.ApplyModifiers:
  283.                 if Config.ExportArmatures:
  284.                     #Create a copy of the object and remove all armature modifiers so an unshaped
  285.                     #mesh can be created from it.
  286.                     Object2 = Object.copy()
  287.                     for Modifier in [Modifier for Modifier in Object2.modifiers if Modifier.type == "ARMATURE"]:
  288.                         Object2.modifiers.remove(Modifier)
  289.                     Mesh = Object2.to_mesh(bpy.context.scene, True, "PREVIEW")
  290.                 else:
  291.                     Mesh = Object.to_mesh(bpy.context.scene, True, "PREVIEW")
  292.             else:
  293.                 Mesh = Object.to_mesh(bpy.context.scene, False, "PREVIEW")
  294.             if Config.Verbose:
  295.                 print("    Done")
  296.                 print("    Writing Mesh...")
  297.             WriteMesh(Config, Object, Mesh)
  298.             if Config.Verbose:
  299.                 print("    Done")
  300.             if Config.ApplyModifiers and Config.ExportArmatures:
  301.                 bpy.data.objects.remove(Object2)
  302.             bpy.data.meshes.remove(Mesh)
  303.  
  304.         Config.Whitespace -= 1
  305.         Config.File.write("{}}} //End of {}\n".format("  " * Config.Whitespace, LegalName(Object.name)))
  306.         if Config.Verbose:
  307.             print("  Done Writing Object: {}".format(Object.name))
  308.  
  309.  
  310. def WriteLocalMatrix(Config, Object):
  311.     LocalMatrix = Object.matrix_local
  312.  
  313.     Config.File.write("{}FrameTransformMatrix {{\n".format("  " * Config.Whitespace))
  314.     Config.Whitespace += 1
  315.     Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format("  " * Config.Whitespace, LocalMatrix[0][0], LocalMatrix[1][0], LocalMatrix[2][0], LocalMatrix[3][0]))
  316.     Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format("  " * Config.Whitespace, LocalMatrix[0][1], LocalMatrix[1][1], LocalMatrix[2][1], LocalMatrix[3][1]))
  317.     Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format("  " * Config.Whitespace, LocalMatrix[0][2], LocalMatrix[1][2], LocalMatrix[2][2], LocalMatrix[3][2]))
  318.     Config.File.write("{}{:9f},{:9f},{:9f},{:9f};;\n".format("  " * Config.Whitespace, LocalMatrix[0][3], LocalMatrix[1][3], LocalMatrix[2][3], LocalMatrix[3][3]))
  319.     Config.Whitespace -= 1
  320.     Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  321.  
  322.  
  323. def WriteArmatureBones(Config, Object, ChildList):
  324.     PoseBones = Object.pose.bones
  325.     for Bone in ChildList:
  326.         if Config.Verbose:
  327.             print("      Writing Bone: {}...".format(Bone.name))
  328.         Config.File.write("{}Frame {} {{\n".format("  " * Config.Whitespace, LegalName(Object.name) + "_" + LegalName(Bone.name)))
  329.         Config.Whitespace += 1
  330.  
  331.         PoseBone = PoseBones[Bone.name]
  332.  
  333.         if Bone.parent:
  334.             BoneMatrix = PoseBone.parent.matrix.inverted()
  335.         else:
  336.             BoneMatrix = Matrix()
  337.  
  338.         BoneMatrix *= PoseBone.matrix
  339.        
  340.         Config.File.write("{}FrameTransformMatrix {{\n".format("  " * Config.Whitespace))
  341.         Config.Whitespace += 1
  342.         Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format("  " * Config.Whitespace, BoneMatrix[0][0], BoneMatrix[1][0], BoneMatrix[2][0], BoneMatrix[3][0]))
  343.         Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format("  " * Config.Whitespace, BoneMatrix[0][1], BoneMatrix[1][1], BoneMatrix[2][1], BoneMatrix[3][1]))
  344.         Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format("  " * Config.Whitespace, BoneMatrix[0][2], BoneMatrix[1][2], BoneMatrix[2][2], BoneMatrix[3][2]))
  345.         Config.File.write("{}{:9f},{:9f},{:9f},{:9f};;\n".format("  " * Config.Whitespace, BoneMatrix[0][3], BoneMatrix[1][3], BoneMatrix[2][3], BoneMatrix[3][3]))
  346.         Config.Whitespace -= 1
  347.         Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  348.  
  349.         if Config.Verbose:
  350.             print("      Done")
  351.         WriteArmatureBones(Config, Object, Bone.children)
  352.         Config.Whitespace -= 1
  353.  
  354.         Config.File.write("{}}} //End of {}\n".format("  " * Config.Whitespace, LegalName(Object.name) + "_" + LegalName(Bone.name)))
  355.  
  356.  
  357. def WriteMesh(Config, Object, Mesh):
  358.     Config.File.write("{}Mesh {{ //{} Mesh\n".format("  " * Config.Whitespace, LegalName(Mesh.name)))
  359.     Config.Whitespace += 1
  360.  
  361.     if Config.Verbose:
  362.         print("      Writing Mesh Vertices...")
  363.     WriteMeshVertices(Config, Mesh)
  364.     if Config.Verbose:
  365.         print("      Done\n      Writing Mesh Normals...")
  366.     WriteMeshNormals(Config, Mesh)
  367.     if Config.Verbose:
  368.         print("      Done\n      Writing Mesh Materials...")
  369.     WriteMeshMaterials(Config, Mesh)
  370.     if Config.Verbose:
  371.         print("      Done")
  372.     if Mesh.uv_textures:
  373.         if Config.Verbose:
  374.             print("      Writing Mesh UV Coordinates...")
  375.         WriteMeshUVCoordinates(Config, Mesh)
  376.         if Config.Verbose:
  377.             print("      Done")
  378.     if Config.ExportArmatures:
  379.         if Config.Verbose:
  380.             print("      Writing Mesh Skin Weights...")
  381.         WriteMeshSkinWeights(Config, Object, Mesh)
  382.         if Config.Verbose:
  383.             print("      Done")
  384.  
  385.     Config.Whitespace -= 1
  386.     Config.File.write("{}}} //End of {} Mesh\n".format("  " * Config.Whitespace, LegalName(Mesh.name)))
  387.  
  388.  
  389. def WriteMeshVertices(Config, Mesh):
  390.     Index = 0
  391.     VertexCount = GetMeshVertexCount(Mesh)
  392.     Config.File.write("{}{};\n".format("  " * Config.Whitespace, VertexCount))
  393.  
  394.     for Polygon in Mesh.polygons:
  395.         Vertices = list(Polygon.vertices)
  396.  
  397.         if Config.CoordinateSystem == 1:
  398.             Vertices = Vertices[::-1]
  399.  
  400.         for Vertex in [Mesh.vertices[Vertex] for Vertex in Vertices]:
  401.             Position = Vertex.co
  402.             Config.File.write("{}{:9f};{:9f};{:9f};".format("  " * Config.Whitespace, Position[0], Position[1], Position[2]))
  403.             Index += 1
  404.             if Index == VertexCount:
  405.                 Config.File.write(";\n")
  406.             else:
  407.                 Config.File.write(",\n")
  408.  
  409.     Index = 0
  410.     Config.File.write("{}{};\n".format("  " * Config.Whitespace, len(Mesh.polygons)))
  411.  
  412.     for Polygon in Mesh.polygons:
  413.         Config.File.write("{}{};".format("  " * Config.Whitespace, len(Polygon.vertices)))
  414.         for Vertex in Polygon.vertices:
  415.             Config.File.write("{};".format(Index))
  416.             Index += 1
  417.         if Index == VertexCount:
  418.             Config.File.write(";\n")
  419.         else:
  420.             Config.File.write(",\n")
  421.  
  422.  
  423. def WriteMeshNormals(Config, Mesh):
  424.     Config.File.write("{}MeshNormals {{ //{} Normals\n".format("  " * Config.Whitespace, LegalName(Mesh.name)))
  425.     Config.Whitespace += 1
  426.  
  427.     Index = 0
  428.     VertexCount = GetMeshVertexCount(Mesh)
  429.     Config.File.write("{}{};\n".format("  " * Config.Whitespace, VertexCount))
  430.  
  431.     for Polygon in Mesh.polygons:
  432.         Vertices = list(Polygon.vertices)
  433.  
  434.         if Config.CoordinateSystem == 1:
  435.             Vertices = Vertices[::-1]
  436.         for Vertex in [Mesh.vertices[Vertex] for Vertex in Vertices]:
  437.             if Polygon.use_smooth:
  438.                 Normal = Vertex.normal
  439.             else:
  440.                 Normal = Polygon.normal
  441.             if Config.FlipNormals:
  442.                 Normal = -Normal
  443.             Config.File.write("{}{:9f};{:9f};{:9f};".format("  " * Config.Whitespace, Normal[0], Normal[1], Normal[2]))
  444.             Index += 1
  445.             if Index == VertexCount:
  446.                 Config.File.write(";\n")
  447.             else:
  448.                 Config.File.write(",\n")
  449.  
  450.     Index = 0
  451.     Config.File.write("{}{};\n".format("  " * Config.Whitespace, len(Mesh.polygons)))
  452.  
  453.     for Polygon in Mesh.polygons:
  454.         Config.File.write("{}{};".format("  " * Config.Whitespace, len(Polygon.vertices)))
  455.         for Vertex in Polygon.vertices:
  456.             Config.File.write("{};".format(Index))
  457.             Index += 1
  458.         if Index == VertexCount:
  459.             Config.File.write(";\n")
  460.         else:
  461.             Config.File.write(",\n")
  462.     Config.Whitespace -= 1
  463.     Config.File.write("{}}} //End of {} Normals\n".format("  " * Config.Whitespace, LegalName(Mesh.name)))
  464.  
  465.  
  466. def WriteMeshMaterials(Config, Mesh):
  467.     Config.File.write("{}MeshMaterialList {{ //{} Material List\n".format("  " * Config.Whitespace, LegalName(Mesh.name)))
  468.     Config.Whitespace += 1
  469.  
  470.     Materials = Mesh.materials
  471.     if Materials.keys():
  472.         MaterialIndexes = {}
  473.         for Polygon in Mesh.polygons:
  474.             if Materials[Polygon.material_index] not in MaterialIndexes:
  475.                 MaterialIndexes[Materials[Polygon.material_index]] = len(MaterialIndexes)
  476.  
  477.         PolygonCount = len(Mesh.polygons)
  478.         Index = 0
  479.         Config.File.write("{}{};\n{}{};\n".format("  " * Config.Whitespace, len(MaterialIndexes), "  " * Config.Whitespace, PolygonCount))
  480.         for Polygon in Mesh.polygons:
  481.             Config.File.write("{}{}".format("  " * Config.Whitespace, MaterialIndexes[Materials[Polygon.material_index]]))
  482.             Index += 1
  483.             if Index == PolygonCount:
  484.                 Config.File.write(";;\n")
  485.             else:
  486.                 Config.File.write(",\n")
  487.  
  488.         Materials = [Item[::-1] for Item in MaterialIndexes.items()]
  489.         Materials.sort()
  490.         for Material in Materials:
  491.             WriteMaterial(Config, Material[1])
  492.     else:
  493.         Config.File.write("{}1;\n{}1;\n{}0;;\n".format("  " * Config.Whitespace, "  " * Config.Whitespace, "  " * Config.Whitespace))
  494.         WriteMaterial(Config)
  495.  
  496.     Config.Whitespace -= 1
  497.     Config.File.write("{}}} //End of {} Material List\n".format("  " * Config.Whitespace, LegalName(Mesh.name)))
  498.  
  499.  
  500. def WriteMaterial(Config, Material=None):
  501.     if Material:
  502.         Config.File.write("{}Material {} {{\n".format("  " * Config.Whitespace, LegalName(Material.name)))
  503.         Config.Whitespace += 1
  504.  
  505.         Diffuse = list(Vector(Material.diffuse_color) * Material.diffuse_intensity)
  506.         Diffuse.append(Material.alpha)
  507.         Specularity = 1000 * (Material.specular_hardness - 1.0) / (511.0 - 1.0) # Map Blender's range of 1 - 511 to 0 - 1000
  508.         Specular = list(Vector(Material.specular_color) * Material.specular_intensity)
  509.  
  510.         Config.File.write("{}{:9f};{:9f};{:9f};{:9f};;\n".format("  " * Config.Whitespace, Diffuse[0], Diffuse[1], Diffuse[2], Diffuse[3]))
  511.         Config.File.write("{} {:9f};\n".format("  " * Config.Whitespace, Specularity))
  512.         Config.File.write("{}{:9f};{:9f};{:9f};;\n".format("  " * Config.Whitespace, Specular[0], Specular[1], Specular[2]))
  513.     else:
  514.         Config.File.write("{}Material Default_Material {{\n".format("  " * Config.Whitespace))
  515.         Config.Whitespace += 1
  516.         Config.File.write("{} 0.800000; 0.800000; 0.800000; 0.800000;;\n".format("  " * Config.Whitespace))
  517.         Config.File.write("{} 96.078431;\n".format("  " * Config.Whitespace)) # 1000 * (50 - 1) / (511 - 1)
  518.         Config.File.write("{} 0.500000; 0.500000; 0.500000;;\n".format("  " * Config.Whitespace))
  519.     Config.File.write("{} 0.000000; 0.000000; 0.000000;;\n".format("  " * Config.Whitespace))
  520.     if Config.ExportTextures:
  521.         TextureFileName = GetMaterialTextureFileName(Material)
  522.         if TextureFileName:
  523.             Config.File.write("{}TextureFilename {{\"{}\";}}\n".format("  " * Config.Whitespace, TextureFileName))
  524.     Config.Whitespace -= 1
  525.     Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  526.  
  527.  
  528. def WriteMeshUVCoordinates(Config, Mesh):
  529.     Config.File.write("{}MeshTextureCoords {{ //{} UV Coordinates\n".format("  " * Config.Whitespace, LegalName(Mesh.name)))
  530.     Config.Whitespace += 1
  531.  
  532.     UVCoordinates = Mesh.uv_layers.active.data
  533.  
  534.     Index = 0
  535.     VertexCount = GetMeshVertexCount(Mesh)
  536.     Config.File.write("{}{};\n".format("  " * Config.Whitespace, VertexCount))
  537.  
  538.     for Polygon in Mesh.polygons:
  539.         Vertices = []
  540.         for Vertex in [UVCoordinates[Vertex] for Vertex in Polygon.loop_indices]:
  541.             Vertices.append(tuple(Vertex.uv))
  542.         if Config.CoordinateSystem == 1:
  543.             Vertices = Vertices[::-1]
  544.         for Vertex in Vertices:
  545.             Config.File.write("{}{:9f};{:9f};".format("  " * Config.Whitespace, Vertex[0], 1 - Vertex[1]))
  546.             Index += 1
  547.             if Index == VertexCount:
  548.                 Config.File.write(";\n")
  549.             else:
  550.                 Config.File.write(",\n")
  551.    
  552.     Config.Whitespace -= 1
  553.     Config.File.write("{}}} //End of {} UV Coordinates\n".format("  " * Config.Whitespace, LegalName(Mesh.name)))
  554.  
  555.  
  556. def WriteMeshSkinWeights(Config, Object, Mesh):
  557.     ArmatureList = [Modifier for Modifier in Object.modifiers if Modifier.type == "ARMATURE"]
  558.     if ArmatureList:
  559.         ArmatureObject = ArmatureList[0].object
  560.         ArmatureBones = ArmatureObject.data.bones
  561.  
  562.         PoseBones = ArmatureObject.pose.bones
  563.  
  564.         MaxInfluences = 0
  565.         UsedBones = set()
  566.         #Maps bones to a list of vertices they affect
  567.         VertexGroups = {}
  568.         ObjectVertexGroups = {i: Group.name for (i, Group) in enumerate(Object.vertex_groups)}
  569.         for Vertex in Mesh.vertices:
  570.             #BoneInfluences contains the bones of the armature that affect the current vertex
  571.             BoneInfluences = [PoseBone for Group in Vertex.groups
  572.                               for PoseBone in (PoseBones.get(ObjectVertexGroups.get(Group.group, "")), )
  573.                               if PoseBone is not None
  574.                               ]
  575.  
  576.             if len(BoneInfluences) > MaxInfluences:
  577.                 MaxInfluences = len(BoneInfluences)
  578.             for Bone in BoneInfluences:
  579.                 UsedBones.add(Bone)
  580.                 if Bone not in VertexGroups:
  581.                     VertexGroups[Bone] = [Vertex]
  582.                 else:
  583.                     VertexGroups[Bone].append(Vertex)
  584.         BoneCount = len(UsedBones)
  585.  
  586.         Config.File.write("{}XSkinMeshHeader {{\n".format("  " * Config.Whitespace))
  587.         Config.Whitespace += 1
  588.         Config.File.write("{}{};\n{}{};\n{}{};\n".format("  " * Config.Whitespace, MaxInfluences, "  " * Config.Whitespace, MaxInfluences * 3, "  " * Config.Whitespace, BoneCount))
  589.         Config.Whitespace -= 1
  590.         Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  591.  
  592.         for Bone in UsedBones:
  593.             VertexCount = 0
  594.             VertexIndexes = [Vertex.index for Vertex in VertexGroups[Bone]]
  595.             for Polygon in Mesh.polygons:
  596.                 for Vertex in Polygon.vertices:
  597.                     if Vertex in VertexIndexes:
  598.                         VertexCount += 1
  599.  
  600.             Config.File.write("{}SkinWeights {{\n".format("  " * Config.Whitespace))
  601.             Config.Whitespace += 1
  602.             Config.File.write("{}\"{}\";\n{}{};\n".format("  " * Config.Whitespace, LegalName(ArmatureObject.name) + "_" + LegalName(Bone.name), "  " * Config.Whitespace, VertexCount))
  603.  
  604.             VertexWeights = []
  605.             Index = 0
  606.             WrittenIndexes = 0
  607.             for Polygon in Mesh.polygons:
  608.                 PolygonVertices = list(Polygon.vertices)
  609.                 if Config.CoordinateSystem == 1:
  610.                     PolygonVertices = PolygonVertices[::-1]
  611.                 for Vertex in PolygonVertices:
  612.                     if Vertex in VertexIndexes:
  613.                         Config.File.write("{}{}".format("  " * Config.Whitespace, Index))
  614.  
  615.                         GroupIndexes = {ObjectVertexGroups.get(Group.group): Index
  616.                                         for Index, Group in enumerate(Mesh.vertices[Vertex].groups)
  617.                                         if ObjectVertexGroups.get(Group.group, "") in PoseBones}
  618.  
  619.                         WeightTotal = 0.0
  620.                         for Weight in (Group.weight for Group in Mesh.vertices[Vertex].groups if ObjectVertexGroups.get(Group.group, "") in PoseBones):
  621.                             WeightTotal += Weight
  622.  
  623.                         if WeightTotal:
  624.                             VertexWeights.append(Mesh.vertices[Vertex].groups[GroupIndexes[Bone.name]].weight / WeightTotal)
  625.                         else:
  626.                             VertexWeights.append(0.0)
  627.  
  628.                         WrittenIndexes += 1
  629.                         if WrittenIndexes == VertexCount:
  630.                             Config.File.write(";\n")
  631.                         else:
  632.                             Config.File.write(",\n")
  633.                     Index += 1
  634.  
  635.             for Index, Weight in enumerate(VertexWeights):
  636.                 Config.File.write("{}{:8f}".format("  " * Config.Whitespace, Weight))
  637.                 if Index == (VertexCount - 1):
  638.                     Config.File.write(";\n")
  639.                 else:
  640.                     Config.File.write(",\n")
  641.            
  642.             RestBone = ArmatureBones[Bone.name]
  643.            
  644.             #BoneMatrix transforms mesh vertices into the space of the bone.
  645.             #Here are the final transformations in order:
  646.             #  - Object Space to World Space
  647.             #  - World Space to Armature Space
  648.             #  - Armature Space to Bone Space (The bone matrix needs to be rotated 90 degrees to align with Blender's world axes)
  649.             #This way, when BoneMatrix is transformed by the bone's Frame matrix, the vertices will be in their final world position.
  650.            
  651.             BoneMatrix = RestBone.matrix_local.inverted()
  652.             BoneMatrix *= ArmatureObject.matrix_world.inverted()
  653.             BoneMatrix *= Object.matrix_world
  654.            
  655.             Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format("  " * Config.Whitespace, BoneMatrix[0][0], BoneMatrix[1][0], BoneMatrix[2][0], BoneMatrix[3][0]))
  656.             Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format("  " * Config.Whitespace, BoneMatrix[0][1], BoneMatrix[1][1], BoneMatrix[2][1], BoneMatrix[3][1]))
  657.             Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format("  " * Config.Whitespace, BoneMatrix[0][2], BoneMatrix[1][2], BoneMatrix[2][2], BoneMatrix[3][2]))
  658.             Config.File.write("{}{:9f},{:9f},{:9f},{:9f};;\n".format("  " * Config.Whitespace, BoneMatrix[0][3], BoneMatrix[1][3], BoneMatrix[2][3], BoneMatrix[3][3]))
  659.             Config.Whitespace -= 1
  660.             Config.File.write("{}}}  //End of {} Skin Weights\n".format("  " * Config.Whitespace, LegalName(ArmatureObject.name) + "_" + LegalName(Bone.name)))
  661.  
  662.  
  663. def WriteKeyedAnimationSet(Config):
  664.     Config.File.write("{}AnimationSet {{\n".format("  " * Config.Whitespace))
  665.     Config.Whitespace += 1
  666.     for Object in [Object for Object in Config.ObjectList if Object.animation_data]:
  667.         if Config.Verbose:
  668.             print("  Writing Animation Data for Object: {}".format(Object.name))
  669.         Action = Object.animation_data.action
  670.         if Action:
  671.             PositionFCurves = [None, None, None]
  672.             RotationFCurves = [None, None, None]
  673.             ScaleFCurves = [None, None, None]
  674.             for FCurve in Action.fcurves:
  675.                 if FCurve.data_path == "location":
  676.                     PositionFCurves[FCurve.array_index] = FCurve
  677.                 elif FCurve.data_path == "rotation_euler":
  678.                     RotationFCurves[FCurve.array_index] = FCurve
  679.                 elif FCurve.data_path == "scale":
  680.                     ScaleFCurves[FCurve.array_index] = FCurve
  681.             if [FCurve for FCurve in PositionFCurves + RotationFCurves + ScaleFCurves if FCurve]:
  682.                 Config.File.write("{}Animation {{\n".format("  " * Config.Whitespace))
  683.                 Config.Whitespace += 1
  684.                 Config.File.write("{}{{{}}}\n".format("  " * Config.Whitespace, LegalName(Object.name)))
  685.  
  686.                 #Position
  687.                 if Config.Verbose:
  688.                     print("    Writing Position...")
  689.                 AllKeyframes = set()
  690.                 for Index, FCurve in enumerate(PositionFCurves):
  691.                     if FCurve:
  692.                         Keyframes = []
  693.                         for Keyframe in FCurve.keyframe_points:
  694.                             if Keyframe.co[0] < bpy.context.scene.frame_start:
  695.                                 AllKeyframes.add(bpy.context.scene.frame_start)
  696.                             elif Keyframe.co[0] > bpy.context.scene.frame_end:
  697.                                 AllKeyframes.add(bpy.context.scene.frame_end)
  698.                             else:
  699.                                 Keyframes.append(Keyframe.co)
  700.                                 AllKeyframes.add(int(Keyframe.co[0]))
  701.                         PositionFCurves[Index] = {int(Keyframe): Value for Keyframe, Value in Keyframes}
  702.                 Config.File.write("{}AnimationKey {{ //Position\n".format("  " * Config.Whitespace))
  703.                 Config.Whitespace += 1
  704.                 AllKeyframes = list(AllKeyframes)
  705.                 AllKeyframes.sort()
  706.                 if len(AllKeyframes):
  707.                     Config.File.write("{}2;\n{}{};\n".format("  " * Config.Whitespace, "  " * Config.Whitespace, len(AllKeyframes)))
  708.                     for Keyframe in AllKeyframes:
  709.                         bpy.context.scene.frame_set(Keyframe)
  710.                         Position = Vector()
  711.                         Position[0] = ((PositionFCurves[0][Keyframe] if Keyframe in PositionFCurves[0] else Object.location[0]) if PositionFCurves[0] else Object.location[0])
  712.                         Position[1] = ((PositionFCurves[1][Keyframe] if Keyframe in PositionFCurves[1] else Object.location[1]) if PositionFCurves[1] else Object.location[1])
  713.                         Position[2] = ((PositionFCurves[2][Keyframe] if Keyframe in PositionFCurves[2] else Object.location[2]) if PositionFCurves[2] else Object.location[2])
  714.                         Config.File.write("{}{}{:9f},{:9f},{:9f};;".format("  " * Config.Whitespace, (str(Keyframe - bpy.context.scene.frame_start) + ";3;").ljust(8), Position[0], Position[1], Position[2]))
  715.                         if Keyframe == AllKeyframes[-1]:
  716.                             Config.File.write(";\n")
  717.                         else:
  718.                             Config.File.write(",\n")
  719.                        
  720.                 else:
  721.                     Config.File.write("{}2;\n{}1;\n".format("  " * Config.Whitespace, "  " * Config.Whitespace))
  722.                     bpy.context.scene.frame_set(bpy.context.scene.frame_start)
  723.                     Position = Object.matrix_local.to_translation()
  724.                     Config.File.write("{}{}{:9f},{:9f},{:9f};;;\n".format("  " * Config.Whitespace, ("0;3;").ljust(8), Position[0], Position[1], Position[2]))
  725.                 Config.Whitespace -= 1
  726.                 Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  727.                 if Config.Verbose:
  728.                     print("    Done")
  729.  
  730.                 #Rotation
  731.                 if Config.Verbose:
  732.                     print("    Writing Rotation...")
  733.                 AllKeyframes = set()
  734.                 for Index, FCurve in enumerate(RotationFCurves):
  735.                     if FCurve:
  736.                         Keyframes = []
  737.                         for Keyframe in FCurve.keyframe_points:
  738.                             if Keyframe.co[0] < bpy.context.scene.frame_start:
  739.                                 AllKeyframes.add(bpy.context.scene.frame_start)
  740.                             elif Keyframe.co[0] > bpy.context.scene.frame_end:
  741.                                 AllKeyframes.add(bpy.context.scene.frame_end)
  742.                             else:
  743.                                 Keyframes.append(Keyframe.co)
  744.                                 AllKeyframes.add(int(Keyframe.co[0]))
  745.                         RotationFCurves[Index] = {int(Keyframe): Value for Keyframe, Value in Keyframes}
  746.                 Config.File.write("{}AnimationKey {{ //Rotation\n".format("  " * Config.Whitespace))
  747.                 Config.Whitespace += 1
  748.                 AllKeyframes = list(AllKeyframes)
  749.                 AllKeyframes.sort()
  750.                 if len(AllKeyframes):
  751.                     Config.File.write("{}0;\n{}{};\n".format("  " * Config.Whitespace, "  " * Config.Whitespace, len(AllKeyframes)))
  752.                     for Keyframe in AllKeyframes:
  753.                         bpy.context.scene.frame_set(Keyframe)
  754.                         Rotation = Euler()
  755.                         Rotation[0] = ((RotationFCurves[0][Keyframe] if Keyframe in RotationFCurves[0] else Object.rotation_euler[0]) if RotationFCurves[0] else Object.rotation_euler[0])
  756.                         Rotation[1] = ((RotationFCurves[1][Keyframe] if Keyframe in RotationFCurves[1] else Object.rotation_euler[1]) if RotationFCurves[1] else Object.rotation_euler[1])
  757.                         Rotation[2] = ((RotationFCurves[2][Keyframe] if Keyframe in RotationFCurves[2] else Object.rotation_euler[2]) if RotationFCurves[2] else Object.rotation_euler[2])
  758.                         Rotation = Rotation.to_quaternion()
  759.                         Config.File.write("{}{}{:9f},{:9f},{:9f},{:9f};;".format("  " * Config.Whitespace, (str(Keyframe - bpy.context.scene.frame_start) + ";4;").ljust(8), -Rotation[0], Rotation[1], Rotation[2], Rotation[3]))
  760.                         if Keyframe == AllKeyframes[-1]:
  761.                             Config.File.write(";\n")
  762.                         else:
  763.                             Config.File.write(",\n")
  764.                 else:
  765.                     Config.File.write("{}0;\n{}1;\n".format("  " * Config.Whitespace, "  " * Config.Whitespace))
  766.                     bpy.context.scene.frame_set(bpy.context.scene.frame_start)
  767.                     Rotation = Object.rotation_euler.to_quaternion()
  768.                     Config.File.write("{}{}{:9f},{:9f},{:9f},{:9f};;;\n".format("  " * Config.Whitespace, ("0;4;").ljust(8), -Rotation[0], Rotation[1], Rotation[2], Rotation[3]))
  769.                 Config.Whitespace -= 1
  770.                 Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  771.                 if Config.Verbose:
  772.                     print("    Done")
  773.  
  774.                 #Scale
  775.                 if Config.Verbose:
  776.                     print("    Writing Scale...")
  777.                 AllKeyframes = set()
  778.                 for Index, FCurve in enumerate(ScaleFCurves):
  779.                     if FCurve:
  780.                         Keyframes = []
  781.                         for Keyframe in FCurve.keyframe_points:
  782.                             if Keyframe.co[0] < bpy.context.scene.frame_start:
  783.                                 AllKeyframes.add(bpy.context.scene.frame_start)
  784.                             elif Keyframe.co[0] > bpy.context.scene.frame_end:
  785.                                 AllKeyframes.add(bpy.context.scene.frame_end)
  786.                             else:
  787.                                 Keyframes.append(Keyframe.co)
  788.                                 AllKeyframes.add(int(Keyframe.co[0]))
  789.                         ScaleFCurves[Index] = {int(Keyframe): Value for Keyframe, Value in Keyframes}
  790.                 Config.File.write("{}AnimationKey {{ //Scale\n".format("  " * Config.Whitespace))
  791.                 Config.Whitespace += 1
  792.                 AllKeyframes = list(AllKeyframes)
  793.                 AllKeyframes.sort()
  794.                 if len(AllKeyframes):
  795.                     Config.File.write("{}1;\n{}{};\n".format("  " * Config.Whitespace, "  " * Config.Whitespace, len(AllKeyframes)))
  796.                     for Keyframe in AllKeyframes:
  797.                         bpy.context.scene.frame_set(Keyframe)
  798.                         Scale = Vector()
  799.                         Scale[0] = ((ScaleFCurves[0][Keyframe] if Keyframe in ScaleFCurves[0] else Object.scale[0]) if ScaleFCurves[0] else Object.scale[0])
  800.                         Scale[1] = ((ScaleFCurves[1][Keyframe] if Keyframe in ScaleFCurves[1] else Object.scale[1]) if ScaleFCurves[1] else Object.scale[1])
  801.                         Scale[2] = ((ScaleFCurves[2][Keyframe] if Keyframe in ScaleFCurves[2] else Object.scale[2]) if ScaleFCurves[2] else Object.scale[2])
  802.                         Config.File.write("{}{}{:9f},{:9f},{:9f};;".format("  " * Config.Whitespace, (str(Keyframe - bpy.context.scene.frame_start) + ";3;").ljust(8), Scale[0], Scale[1], Scale[2]))
  803.                         if Keyframe == AllKeyframes[-1]:
  804.                             Config.File.write(";\n")
  805.                         else:
  806.                             Config.File.write(",\n")
  807.                 else:
  808.                     Config.File.write("{}1;\n{}1;\n".format("  " * Config.Whitespace, "  " * Config.Whitespace))
  809.                     bpy.context.scene.frame_set(bpy.context.scene.frame_start)
  810.                     Scale = Object.matrix_local.to_scale()
  811.                     Config.File.write("{}{}{:9f},{:9f},{:9f};;;\n".format("  " * Config.Whitespace, ("0;3;").ljust(8), Scale[0], Scale[1], Scale[2]))
  812.                 Config.Whitespace -= 1
  813.                 Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  814.                 if Config.Verbose:
  815.                     print("    Done")
  816.  
  817.                 Config.Whitespace -= 1
  818.                 Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  819.             else:
  820.                 if Config.Verbose:
  821.                     print("    Object has no useable animation data.")
  822.  
  823.             if Config.ExportArmatures and Object.type == "ARMATURE":
  824.                 if Config.Verbose:
  825.                     print("    Writing Armature Bone Animation Data...")
  826.                 PoseBones = Object.pose.bones
  827.                 for Bone in PoseBones:
  828.                     if Config.Verbose:
  829.                         print("      Writing Bone: {}...".format(Bone.name))
  830.                     PositionFCurves = [None, None, None]
  831.                     RotationFCurves = [None, None, None, None]
  832.                     ScaleFCurves = [None, None, None]
  833.                     for FCurve in Action.fcurves:
  834.                         if FCurve.data_path == "pose.bones[\"{}\"].location".format(Bone.name):
  835.                             PositionFCurves[FCurve.array_index] = FCurve
  836.                         elif FCurve.data_path == "pose.bones[\"{}\"].rotation_quaternion".format(Bone.name):
  837.                             RotationFCurves[FCurve.array_index] = FCurve
  838.                         elif FCurve.data_path == "pose.bones[\"{}\"].scale".format(Bone.name):
  839.                             ScaleFCurves[FCurve.array_index] = FCurve
  840.                     if not [FCurve for FCurve in PositionFCurves + RotationFCurves + ScaleFCurves if FCurve]:
  841.                         if Config.Verbose:
  842.                             print("        Bone has no useable animation data.\n      Done")
  843.                         continue
  844.  
  845.                     Config.File.write("{}Animation {{\n".format("  " * Config.Whitespace))
  846.                     Config.Whitespace += 1
  847.                     Config.File.write("{}{{{}}}\n".format("  " * Config.Whitespace, LegalName(Object.name) + "_" + LegalName(Bone.name)))
  848.  
  849.                     #Position
  850.                     if Config.Verbose:
  851.                         print("        Writing Position...")
  852.                     AllKeyframes = set()
  853.                     for Index, FCurve in enumerate(PositionFCurves):
  854.                         if FCurve:
  855.                             Keyframes = []
  856.                             for Keyframe in FCurve.keyframe_points:
  857.                                 if Keyframe.co[0] < bpy.context.scene.frame_start:
  858.                                     AllKeyframes.add(bpy.context.scene.frame_start)
  859.                                 elif Keyframe.co[0] > bpy.context.scene.frame_end:
  860.                                     AllKeyframes.add(bpy.context.scene.frame_end)
  861.                                 else:
  862.                                     Keyframes.append(Keyframe.co)
  863.                                     AllKeyframes.add(int(Keyframe.co[0]))
  864.                             PositionFCurves[Index] = {int(Keyframe): Value for Keyframe, Value in Keyframes}
  865.                     Config.File.write("{}AnimationKey {{ //Position\n".format("  " * Config.Whitespace))
  866.                     Config.Whitespace += 1
  867.                     AllKeyframes = list(AllKeyframes)
  868.                     AllKeyframes.sort()
  869.                     if not len(AllKeyframes):
  870.                         AllKeyframes = [bpy.context.scene.frame_start]
  871.                     Config.File.write("{}2;\n{}{};\n".format("  " * Config.Whitespace, "  " * Config.Whitespace, len(AllKeyframes)))
  872.                     for Keyframe in AllKeyframes:
  873.                         bpy.context.scene.frame_set(Keyframe)
  874.                        
  875.                         if Bone.parent:
  876.                             PoseMatrix = Bone.parent.matrix.inverted()
  877.                         else:
  878.                             PoseMatrix = Matrix()
  879.                         PoseMatrix *= Bone.matrix
  880.                        
  881.                         Position = PoseMatrix.to_translation()
  882.                         Config.File.write("{}{}{:9f},{:9f},{:9f};;".format("  " * Config.Whitespace, (str(Keyframe - bpy.context.scene.frame_start) + ";3;").ljust(8), Position[0], Position[1], Position[2]))
  883.                         if Keyframe == AllKeyframes[-1]:
  884.                             Config.File.write(";\n")
  885.                         else:
  886.                             Config.File.write(",\n")
  887.                     Config.Whitespace -= 1
  888.                     Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  889.                     if Config.Verbose:
  890.                         print("        Done")
  891.  
  892.                     #Rotation
  893.                     if Config.Verbose:
  894.                         print("        Writing Rotation...")
  895.                     AllKeyframes = set()
  896.                     for Index, FCurve in enumerate(RotationFCurves):
  897.                         if FCurve:
  898.                             Keyframes = []
  899.                             for Keyframe in FCurve.keyframe_points:
  900.                                 if Keyframe.co[0] < bpy.context.scene.frame_start:
  901.                                     AllKeyframes.add(bpy.context.scene.frame_start)
  902.                                 elif Keyframe.co[0] > bpy.context.scene.frame_end:
  903.                                     AllKeyframes.add(bpy.context.scene.frame_end)
  904.                                 else:
  905.                                     Keyframes.append(Keyframe.co)
  906.                                     AllKeyframes.add(int(Keyframe.co[0]))
  907.                             RotationFCurves[Index] = {int(Keyframe): Value for Keyframe, Value in Keyframes}
  908.                     Config.File.write("{}AnimationKey {{ //Rotation\n".format("  " * Config.Whitespace))
  909.                     Config.Whitespace += 1
  910.                     AllKeyframes = list(AllKeyframes)
  911.                     AllKeyframes.sort()
  912.                     if not len(AllKeyframes):
  913.                         AllKeyframes = [bpy.context.scene.frame_start]
  914.                     Config.File.write("{}0;\n{}{};\n".format("  " * Config.Whitespace, "  " * Config.Whitespace, len(AllKeyframes)))
  915.                     for Keyframe in AllKeyframes:
  916.                         bpy.context.scene.frame_set(Keyframe)
  917.                        
  918.                         if Bone.parent:
  919.                             PoseMatrix = Bone.parent.matrix.inverted()
  920.                         else:
  921.                             PoseMatrix = Matrix()
  922.                         PoseMatrix *= Bone.matrix
  923.                        
  924.                         Rotation = PoseMatrix.to_3x3().to_quaternion()
  925.                         Config.File.write("{}{}{:9f},{:9f},{:9f},{:9f};;".format("  " * Config.Whitespace, (str(Keyframe - bpy.context.scene.frame_start) + ";4;").ljust(8), -Rotation[0], Rotation[1], Rotation[2], Rotation[3]))
  926.                         if Keyframe == AllKeyframes[-1]:
  927.                             Config.File.write(";\n")
  928.                         else:
  929.                             Config.File.write(",\n")
  930.                     Config.Whitespace -= 1
  931.                     Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  932.                     if Config.Verbose:
  933.                         print("        Done")
  934.  
  935.                     #Scale
  936.                     if Config.Verbose:
  937.                         print("        Writing Scale...")
  938.                     AllKeyframes = set()
  939.                     for Index, FCurve in enumerate(ScaleFCurves):
  940.                         if FCurve:
  941.                             Keyframes = []
  942.                             for Keyframe in FCurve.keyframe_points:
  943.                                 if Keyframe.co[0] < bpy.context.scene.frame_start:
  944.                                     AllKeyframes.add(bpy.context.scene.frame_start)
  945.                                 elif Keyframe.co[0] > bpy.context.scene.frame_end:
  946.                                     AllKeyframes.add(bpy.context.scene.frame_end)
  947.                                 else:
  948.                                     Keyframes.append(Keyframe.co)
  949.                                     AllKeyframes.add(int(Keyframe.co[0]))
  950.                             ScaleFCurves[Index] = {int(Keyframe): Value for Keyframe, Value in Keyframes}
  951.                     Config.File.write("{}AnimationKey {{ //Scale\n".format("  " * Config.Whitespace))
  952.                     Config.Whitespace += 1
  953.                     AllKeyframes = list(AllKeyframes)
  954.                     AllKeyframes.sort()
  955.                     if not len(AllKeyframes):
  956.                         AllKeyframes = [bpy.context.scene.frame_start]
  957.                     Config.File.write("{}1;\n{}{};\n".format("  " * Config.Whitespace, "  " * Config.Whitespace, len(AllKeyframes)))
  958.                     for Keyframe in AllKeyframes:
  959.                         bpy.context.scene.frame_set(Keyframe)
  960.                        
  961.                         if Bone.parent:
  962.                             PoseMatrix = Bone.parent.matrix.inverted()
  963.                         else:
  964.                             PoseMatrix = Matrix()
  965.                         PoseMatrix *= Bone.matrix
  966.                        
  967.                         Scale = PoseMatrix.to_scale()
  968.                         Config.File.write("{}{}{:9f},{:9f},{:9f};;".format("  " * Config.Whitespace, (str(Keyframe - bpy.context.scene.frame_start) + ";3;").ljust(8), Scale[0], Scale[1], Scale[2]))
  969.                         if Keyframe == AllKeyframes[-1]:
  970.                             Config.File.write(";\n")
  971.                         else:
  972.                             Config.File.write(",\n")
  973.                     Config.Whitespace -= 1
  974.                     Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  975.                     if Config.Verbose:
  976.                         print("        Done")
  977.  
  978.                     Config.Whitespace -= 1
  979.                     Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  980.                     if Config.Verbose:
  981.                         print("      Done") #Done with Armature Bone
  982.                 if Config.Verbose:
  983.                     print("    Done") #Done with Armature Bone data
  984.         if Config.Verbose:
  985.             print("  Done") #Done with Object
  986.  
  987.     Config.Whitespace -= 1
  988.     Config.File.write("{}}} //End of AnimationSet\n".format("  " * Config.Whitespace))
  989.  
  990. def WriteFullAnimationSet(Config):
  991.     Config.File.write("{}AnimationSet {{\n".format("  " * Config.Whitespace))
  992.     Config.Whitespace += 1
  993.    
  994.     KeyframeCount = bpy.context.scene.frame_end - bpy.context.scene.frame_start + 1
  995.    
  996.     for Object in Config.ObjectList:
  997.         if Config.Verbose:
  998.             print("  Writing Animation Data for Object: {}".format(Object.name))
  999.        
  1000.         Config.File.write("{}Animation {{\n".format("  " * Config.Whitespace))
  1001.         Config.Whitespace += 1
  1002.         Config.File.write("{}{{{}}}\n".format("  " * Config.Whitespace, LegalName(Object.name)))
  1003.        
  1004.         #Position
  1005.         if Config.Verbose:
  1006.             print("    Writing Position...")
  1007.         Config.File.write("{}AnimationKey {{ //Position\n".format("  " * Config.Whitespace))
  1008.         Config.Whitespace += 1
  1009.         Config.File.write("{}2;\n{}{};\n".format("  " * Config.Whitespace, "  " * Config.Whitespace, KeyframeCount))
  1010.         for Frame in range(0, KeyframeCount):
  1011.             bpy.context.scene.frame_set(Frame + bpy.context.scene.frame_start)
  1012.             Position = Object.matrix_local.to_translation()
  1013.             Config.File.write("{}{}{:9f},{:9f},{:9f};;".format("  " * Config.Whitespace, (str(Frame) + ";3;").ljust(8), Position[0], Position[1], Position[2]))
  1014.             if Frame == KeyframeCount-1:
  1015.                 Config.File.write(";\n")
  1016.             else:
  1017.                 Config.File.write(",\n")
  1018.         Config.Whitespace -= 1
  1019.         Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  1020.         if Config.Verbose:
  1021.             print("    Done")
  1022.        
  1023.         #Rotation
  1024.         if Config.Verbose:
  1025.             print("    Writing Rotation...")
  1026.         Config.File.write("{}AnimationKey {{ //Rotation\n".format("  " * Config.Whitespace))
  1027.         Config.Whitespace += 1
  1028.         Config.File.write("{}0;\n{}{};\n".format("  " * Config.Whitespace, "  " * Config.Whitespace, KeyframeCount))
  1029.         for Frame in range(0, KeyframeCount):
  1030.             bpy.context.scene.frame_set(Frame + bpy.context.scene.frame_start)
  1031.             Rotation = Object.rotation_euler.to_quaternion()
  1032.             Config.File.write("{}{}{:9f},{:9f},{:9f},{:9f};;".format("  " * Config.Whitespace, (str(Frame) + ";4;").ljust(8), -Rotation[0], Rotation[1], Rotation[2], Rotation[3]))
  1033.             if Frame == KeyframeCount-1:
  1034.                 Config.File.write(";\n")
  1035.             else:
  1036.                 Config.File.write(",\n")
  1037.         Config.Whitespace -= 1
  1038.         Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  1039.         if Config.Verbose:
  1040.             print("    Done")
  1041.        
  1042.         #Scale
  1043.         if Config.Verbose:
  1044.             print("    Writing Scale...")
  1045.         Config.File.write("{}AnimationKey {{ //Scale\n".format("  " * Config.Whitespace))
  1046.         Config.Whitespace += 1
  1047.         Config.File.write("{}1;\n{}{};\n".format("  " * Config.Whitespace, "  " * Config.Whitespace, KeyframeCount))
  1048.         for Frame in range(0, KeyframeCount):
  1049.             bpy.context.scene.frame_set(Frame + bpy.context.scene.frame_start)
  1050.             Scale = Object.matrix_local.to_scale()
  1051.             Config.File.write("{}{}{:9f},{:9f},{:9f};;".format("  " * Config.Whitespace, (str(Frame) + ";3;").ljust(8), Scale[0], Scale[1], Scale[2]))
  1052.             if Frame == KeyframeCount-1:
  1053.                 Config.File.write(";\n")
  1054.             else:
  1055.                 Config.File.write(",\n")
  1056.         Config.Whitespace -= 1
  1057.         Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  1058.         if Config.Verbose:
  1059.             print("    Done")
  1060.        
  1061.         Config.Whitespace -= 1
  1062.         Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  1063.        
  1064.         if Config.ExportArmatures and Object.type == "ARMATURE":
  1065.             if Config.Verbose:
  1066.                 print("    Writing Armature Bone Animation Data...")
  1067.             PoseBones = Object.pose.bones
  1068.             Bones = Object.data.bones
  1069.             for Bone in PoseBones:
  1070.                 if Config.Verbose:
  1071.                     print("      Writing Bone: {}...".format(Bone.name))
  1072.                
  1073.                 Config.File.write("{}Animation {{\n".format("  " * Config.Whitespace))
  1074.                 Config.Whitespace += 1
  1075.                 Config.File.write("{}{{{}}}\n".format("  " * Config.Whitespace, LegalName(Object.name) + "_" + LegalName(Bone.name)))
  1076.                
  1077.                 #Position
  1078.                 if Config.Verbose:
  1079.                     print("        Writing Position...")
  1080.                 Config.File.write("{}AnimationKey {{ //Position\n".format("  " * Config.Whitespace))
  1081.                 Config.Whitespace += 1
  1082.                 Config.File.write("{}2;\n{}{};\n".format("  " * Config.Whitespace, "  " * Config.Whitespace, KeyframeCount))
  1083.                 for Frame in range(0, KeyframeCount):
  1084.                     bpy.context.scene.frame_set(Frame + bpy.context.scene.frame_start)
  1085.                    
  1086.                     if Bone.parent:
  1087.                         PoseMatrix = Bone.parent.matrix.inverted()
  1088.                     else:
  1089.                         PoseMatrix = Matrix()
  1090.                     PoseMatrix *= Bone.matrix
  1091.                    
  1092.                     Position = PoseMatrix.to_translation()
  1093.                     Config.File.write("{}{}{:9f},{:9f},{:9f};;".format("  " * Config.Whitespace, (str(Frame) + ";3;").ljust(8), Position[0], Position[1], Position[2]))
  1094.                     if Frame == KeyframeCount-1:
  1095.                         Config.File.write(";\n")
  1096.                     else:
  1097.                         Config.File.write(",\n")
  1098.                 Config.Whitespace -= 1
  1099.                 Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  1100.                 if Config.Verbose:
  1101.                     print("        Done")
  1102.                
  1103.                 #Rotation
  1104.                 if Config.Verbose:
  1105.                     print("        Writing Rotation...")
  1106.                 Config.File.write("{}AnimationKey {{ //Rotation\n".format("  " * Config.Whitespace))
  1107.                 Config.Whitespace += 1
  1108.                 Config.File.write("{}0;\n{}{};\n".format("  " * Config.Whitespace, "  " * Config.Whitespace, KeyframeCount))
  1109.                 for Frame in range(0, KeyframeCount):
  1110.                     bpy.context.scene.frame_set(Frame + bpy.context.scene.frame_start)
  1111.                    
  1112.                     Rotation = Bones[Bone.name].matrix.to_quaternion() * Bone.rotation_quaternion
  1113.                    
  1114.                     Config.File.write("{}{}{:9f},{:9f},{:9f},{:9f};;".format("  " * Config.Whitespace, (str(Frame) + ";4;").ljust(8), -Rotation[0], Rotation[1], Rotation[2], Rotation[3]))
  1115.                     if Frame == KeyframeCount-1:
  1116.                         Config.File.write(";\n")
  1117.                     else:
  1118.                         Config.File.write(",\n")
  1119.                 Config.Whitespace -= 1
  1120.                 Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  1121.                 if Config.Verbose:
  1122.                     print("        Done")
  1123.                
  1124.                 #Scale
  1125.                 if Config.Verbose:
  1126.                     print("        Writing Scale...")
  1127.                 Config.File.write("{}AnimationKey {{ //Scale\n".format("  " * Config.Whitespace, KeyframeCount))
  1128.                 Config.Whitespace += 1
  1129.                 Config.File.write("{}1;\n{}{};\n".format("  " * Config.Whitespace, "  " * Config.Whitespace, KeyframeCount))
  1130.                 for Frame in range(0, KeyframeCount):
  1131.                     bpy.context.scene.frame_set(Frame + bpy.context.scene.frame_start)
  1132.                    
  1133.                     if Bone.parent:
  1134.                         PoseMatrix = Bone.parent.matrix.inverted()
  1135.                     else:
  1136.                         PoseMatrix = Matrix()
  1137.                     PoseMatrix *= Bone.matrix
  1138.                    
  1139.                     Scale = PoseMatrix.to_scale()
  1140.                     Config.File.write("{}{}{:9f},{:9f},{:9f};;".format("  " * Config.Whitespace, (str(Frame) + ";3;").ljust(8), Scale[0], Scale[1], Scale[2]))
  1141.                     if Frame == KeyframeCount-1:
  1142.                         Config.File.write(";\n")
  1143.                     else:
  1144.                         Config.File.write(",\n")
  1145.                 Config.Whitespace -= 1
  1146.                 Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  1147.                 if Config.Verbose:
  1148.                     print("        Done")
  1149.                
  1150.                 Config.Whitespace -= 1
  1151.                 Config.File.write("{}}}\n".format("  " * Config.Whitespace))
  1152.                 if Config.Verbose:
  1153.                     print("      Done") #Done with Armature Bone
  1154.             if Config.Verbose:
  1155.                 print("    Done") #Done with Armature Bone data
  1156.         if Config.Verbose:
  1157.             print("  Done")  #Done with Object
  1158.    
  1159.     Config.Whitespace -= 1
  1160.     Config.File.write("{}}} //End of AnimationSet\n".format("  " * Config.Whitespace))
  1161.  
  1162.  
  1163. def CloseFile(Config):
  1164.     if Config.Verbose:
  1165.         print("Closing File...")
  1166.     Config.File.close()
  1167.     if Config.Verbose:
  1168.         print("Done")
  1169.  
  1170.  
  1171. CoordinateSystems = (
  1172.     ("1", "Left-Handed", ""),
  1173.     ("2", "Right-Handed", ""),
  1174.     )
  1175.  
  1176.  
  1177. AnimationModes = (
  1178.     ("0", "None", ""),
  1179.     ("1", "Keyframes Only", ""),
  1180.     ("2", "Full Animation", ""),
  1181.     )
  1182.  
  1183. ExportModes = (
  1184.     ("1", "All Objects", ""),
  1185.     ("2", "Selected Objects", ""),
  1186.     )
  1187.  
  1188.  
  1189. from bpy.props import StringProperty, EnumProperty, BoolProperty
  1190.  
  1191.  
  1192. class DirectXExporter(bpy.types.Operator):
  1193.     """Export to the DirectX model format (.x)"""
  1194.  
  1195.     bl_idname = "export.directx"
  1196.     bl_label = "Export DirectX"
  1197.  
  1198.     filepath = StringProperty(subtype='FILE_PATH')
  1199.  
  1200.     #Coordinate System
  1201.     CoordinateSystem = EnumProperty(
  1202.         name="System",
  1203.         description="Select a coordinate system to export to",
  1204.         items=CoordinateSystems,
  1205.         default="1")
  1206.  
  1207.     #General Options
  1208.     RotateX = BoolProperty(
  1209.         name="Rotate X 90 Degrees",
  1210.         description="Rotate the entire scene 90 degrees around the X axis so Y is up",
  1211.         default=True)
  1212.     FlipNormals = BoolProperty(
  1213.         name="Flip Normals",
  1214.         description="",
  1215.         default=False)
  1216.     ApplyModifiers = BoolProperty(
  1217.         name="Apply Modifiers",
  1218.         description="Apply object modifiers before export",
  1219.         default=False)
  1220.     IncludeFrameRate = BoolProperty(
  1221.         name="Include Frame Rate",
  1222.         description="Include the AnimTicksPerSecond template which is used by " \
  1223.                     "some engines to control animation speed",
  1224.         default=False)
  1225.     ExportTextures = BoolProperty(
  1226.         name="Export Textures",
  1227.         description="Reference external image files to be used by the model",
  1228.         default=True)
  1229.     ExportArmatures = BoolProperty(
  1230.         name="Export Armatures",
  1231.         description="Export the bones of any armatures to deform meshes",
  1232.         default=False)
  1233.     ExportAnimation = EnumProperty(
  1234.         name="Animations",
  1235.         description="Select the type of animations to export. Only object " \
  1236.                     "and armature bone animations can be exported. Full " \
  1237.                     "Animation exports every frame",
  1238.         items=AnimationModes,
  1239.         default="0")
  1240.  
  1241.     #Export Mode
  1242.     ExportMode = EnumProperty(
  1243.         name="Export",
  1244.         description="Select which objects to export. Only Mesh, Empty, " \
  1245.                     "and Armature objects will be exported",
  1246.         items=ExportModes,
  1247.         default="1")
  1248.  
  1249.     Verbose = BoolProperty(
  1250.         name="Verbose",
  1251.         description="Run the exporter in debug mode. Check the console for output",
  1252.         default=False)
  1253.  
  1254.     def execute(self, context):
  1255.         #Append .x
  1256.         FilePath = bpy.path.ensure_ext(self.filepath, ".x")
  1257.  
  1258.         Config = DirectXExporterSettings(context,
  1259.                                          FilePath,
  1260.                                          CoordinateSystem=self.CoordinateSystem,
  1261.                                          RotateX=self.RotateX,
  1262.                                          FlipNormals=self.FlipNormals,
  1263.                                          ApplyModifiers=self.ApplyModifiers,
  1264.                                          IncludeFrameRate=self.IncludeFrameRate,
  1265.                                          ExportTextures=self.ExportTextures,
  1266.                                          ExportArmatures=self.ExportArmatures,
  1267.                                          ExportAnimation=self.ExportAnimation,
  1268.                                          ExportMode=self.ExportMode,
  1269.                                          Verbose=self.Verbose)
  1270.  
  1271.         ExportDirectX(Config)
  1272.         return {'FINISHED'}
  1273.  
  1274.     def invoke(self, context, event):
  1275.         if not self.filepath:
  1276.             self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".x")
  1277.         WindowManager = context.window_manager
  1278.         WindowManager.fileselect_add(self)
  1279.         return {"RUNNING_MODAL"}
  1280.  
  1281.  
  1282. def menu_func(self, context):
  1283.     self.layout.operator(DirectXExporter.bl_idname, text="DirectX (.x)")
  1284.  
  1285.  
  1286. def register():
  1287.     bpy.utils.register_module(__name__)
  1288.  
  1289.     bpy.types.INFO_MT_file_export.append(menu_func)
  1290.  
  1291.  
  1292. def unregister():
  1293.     bpy.utils.unregister_module(__name__)
  1294.  
  1295.     bpy.types.INFO_MT_file_export.remove(menu_func)
  1296.  
  1297.  
  1298. if __name__ == "__main__":
  1299.     register()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement