Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'io_export_directx_x.py')
-rw-r--r--io_export_directx_x.py1229
1 files changed, 1229 insertions, 0 deletions
diff --git a/io_export_directx_x.py b/io_export_directx_x.py
new file mode 100644
index 00000000..76f62d42
--- /dev/null
+++ b/io_export_directx_x.py
@@ -0,0 +1,1229 @@
+# ***** GPL LICENSE BLOCK *****
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# All rights reserved.
+# ***** GPL LICENSE BLOCK *****
+
+bl_addon_info = {
+ "name": "Export DirectX Model Format (.x)",
+ "author": "Chris Foster (Kira Vakaan)",
+ "version": (2, 0),
+ "blender": (2, 5, 5),
+ "api": 33427,
+ "location": "File > Export",
+ "description": "Export to the DirectX Model Format (.x)",
+ "warning": "",
+ "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"\
+ "Scripts/Import-Export/DirectX_Exporter",
+ "tracker_url": "https://projects.blender.org/tracker/index.php?"\
+ "func=detail&aid=22795&group_id=153&atid=469",
+ "category": "Import-Export"}
+
+import os
+from math import radians
+
+import bpy
+from mathutils import *
+
+#Container for the exporter settings
+class DirectXExporterSettings:
+ def __init__(self,
+ context,
+ FilePath,
+ CoordinateSystem=1,
+ RotateX=True,
+ FlipNormals=False,
+ ApplyModifiers=False,
+ IncludeFrameRate=False,
+ ExportTextures=True,
+ ExportArmatures=False,
+ ExportAnimation=0,
+ ExportMode=1,
+ Verbose=False):
+ self.context = context
+ self.FilePath = FilePath
+ self.CoordinateSystem = int(CoordinateSystem)
+ self.RotateX = RotateX
+ self.FlipNormals = FlipNormals
+ self.ApplyModifiers = ApplyModifiers
+ self.IncludeFrameRate = IncludeFrameRate
+ self.ExportTextures = ExportTextures
+ self.ExportArmatures = ExportArmatures
+ self.ExportAnimation = int(ExportAnimation)
+ self.ExportMode = int(ExportMode)
+ self.Verbose = Verbose
+
+
+def LegalName(Name):
+ NewName = Name.replace(".", "_")
+ NewName = NewName.replace(" ", "_")
+ if NewName[0].isdigit() or NewName in ["ARRAY",
+ "DWORD",
+ "UCHAR",
+ "BINARY",
+ "FLOAT",
+ "ULONGLONG",
+ "BINARY_RESOURCE",
+ "SDWORD",
+ "UNICODE",
+ "CHAR",
+ "STRING",
+ "WORD",
+ "CSTRING",
+ "SWORD",
+ "DOUBLE",
+ "TEMPLATE"]:
+ NewName = "_" + NewName
+ return NewName
+
+
+def ExportDirectX(Config):
+ print("----------\nExporting to {}".format(Config.FilePath))
+ if Config.Verbose:
+ print("Opening File...")
+ Config.File = open(Config.FilePath, "w")
+ if Config.Verbose:
+ print("Done")
+
+ if Config.Verbose:
+ print("Generating Object list for export...")
+ if Config.ExportMode == 1:
+ Config.ExportList = [Object for Object in Config.context.scene.objects
+ if Object.type in ("ARMATURE", "EMPTY", "MESH")
+ and Object.parent is None]
+ else:
+ ExportList = [Object for Object in Config.context.selected_objects
+ if Object.type in ("ARMATURE", "EMPTY", "MESH")]
+ Config.ExportList = [Object for Object in ExportList
+ if Object.parent not in ExportList]
+ if Config.Verbose:
+ print("Done")
+
+ if Config.Verbose:
+ print("Setting up...")
+ Config.SystemMatrix = Matrix()
+ if Config.RotateX:
+ Config.SystemMatrix *= Matrix.Rotation(radians(-90), 4, "X")
+ if Config.CoordinateSystem == 1:
+ Config.SystemMatrix *= Matrix.Scale(-1, 4, Vector((0, 1, 0)))
+
+ if Config.ExportAnimation:
+ CurrentFrame = bpy.context.scene.frame_current
+ bpy.context.scene.frame_current = bpy.context.scene.frame_current
+ if Config.Verbose:
+ print("Done")
+
+ if Config.Verbose:
+ print("Writing Header...")
+ WriteHeader(Config)
+ if Config.Verbose:
+ print("Done")
+
+ Config.Whitespace = 0
+ if Config.Verbose:
+ print("Writing Root Frame...")
+ WriteRootFrame(Config)
+ if Config.Verbose:
+ print("Done")
+
+ Config.ObjectList = []
+ if Config.Verbose:
+ print("Writing Objects...")
+ WriteObjects(Config, Config.ExportList)
+ if Config.Verbose:
+ print("Done")
+
+ Config.Whitespace -= 1
+ Config.File.write("{}}} //End of Root Frame\n".format(" " * Config.Whitespace))
+
+ if Config.ExportAnimation:
+ if Config.IncludeFrameRate:
+ if Config.Verbose:
+ print("Writing Frame Rate...")
+ Config.File.write("{}AnimTicksPerSecond {{\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{}{};\n".format(" " * Config.Whitespace, int(bpy.context.scene.render.fps / bpy.context.scene.render.fps_base)))
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ if Config.Verbose:
+ print("Done")
+ if Config.Verbose:
+ print("Writing Animation...")
+ if Config.ExportAnimation==1:
+ WriteKeyedAnimationSet(Config)
+ else:
+ WriteFullAnimationSet(Config)
+ bpy.context.scene.frame_current = CurrentFrame
+ if Config.Verbose:
+ print("Done")
+
+ CloseFile(Config)
+ print("Finished")
+
+
+def GetObjectChildren(Parent):
+ return [Object for Object in Parent.children
+ if Object.type in ("ARMATURE", "EMPTY", "MESH")]
+
+#Returns the vertex count of Mesh, counting each vertex for every face.
+def GetMeshVertexCount(Mesh):
+ VertexCount = 0
+ for Face in Mesh.faces:
+ VertexCount += len(Face.vertices)
+ return VertexCount
+
+#Returns the file path of first image texture from Material.
+def GetMaterialTexture(Material):
+ if Material:
+ #Create a list of Textures that have type "IMAGE"
+ ImageTextures = [Material.texture_slots[TextureSlot].texture for TextureSlot in Material.texture_slots.keys() if Material.texture_slots[TextureSlot].texture.type == "IMAGE"]
+ #Refine a new list with only image textures that have a file source
+ ImageFiles = [os.path.basename(Texture.image.filepath) for Texture in ImageTextures if Texture.image.source == "FILE"]
+ if ImageFiles:
+ return ImageFiles[0]
+ return None
+
+
+def WriteHeader(Config):
+ Config.File.write("xof 0303txt 0032\n\n")
+ if Config.ExportArmatures:
+ Config.File.write("template XSkinMeshHeader {\n\
+ <3cf169ce-ff7c-44ab-93c0-f78f62d172e2>\n\
+ WORD nMaxSkinWeightsPerVertex;\n\
+ WORD nMaxSkinWeightsPerFace;\n\
+ WORD nBones;\n\
+}\n\n\
+template SkinWeights {\n\
+ <6f0d123b-bad2-4167-a0d0-80224f25fabb>\n\
+ STRING transformNodeName;\n\
+ DWORD nWeights;\n\
+ array DWORD vertexIndices[nWeights];\n\
+ array float weights[nWeights];\n\
+ Matrix4x4 matrixOffset;\n\
+}\n\n")
+
+def WriteRootFrame(Config):
+ Config.File.write("{}Frame Root {{\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+
+ Config.File.write("{}FrameTransformMatrix {{\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format(" " * Config.Whitespace, Config.SystemMatrix[0][0], Config.SystemMatrix[0][1], Config.SystemMatrix[0][2], Config.SystemMatrix[0][3]))
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format(" " * Config.Whitespace, Config.SystemMatrix[1][0], Config.SystemMatrix[1][1], Config.SystemMatrix[1][2], Config.SystemMatrix[1][3]))
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format(" " * Config.Whitespace, Config.SystemMatrix[2][0], Config.SystemMatrix[2][1], Config.SystemMatrix[2][2], Config.SystemMatrix[2][3]))
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f};;\n".format(" " * Config.Whitespace, Config.SystemMatrix[3][0], Config.SystemMatrix[3][1], Config.SystemMatrix[3][2], Config.SystemMatrix[3][3]))
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+
+def WriteObjects(Config, ObjectList):
+ Config.ObjectList += ObjectList
+
+ for Object in ObjectList:
+ if Config.Verbose:
+ print(" Writing Object: {}...".format(Object.name))
+ Config.File.write("{}Frame {} {{\n".format(" " * Config.Whitespace, LegalName(Object.name)))
+
+ Config.Whitespace += 1
+ if Config.Verbose:
+ print(" Writing Local Matrix...")
+ WriteLocalMatrix(Config, Object)
+ if Config.Verbose:
+ print(" Done")
+
+ 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...")
+ WriteArmatureBones(Config, Object, ParentList)
+ if Config.Verbose:
+ print(" Done")
+
+ ChildList = GetObjectChildren(Object)
+ if Config.Verbose:
+ print(" Writing Children...")
+ WriteObjects(Config, ChildList)
+ if Config.Verbose:
+ print(" Done Writing Children")
+
+ if Object.type == "MESH":
+ if Config.Verbose:
+ print(" Generating Mesh...")
+ if Config.ApplyModifiers:
+ if Config.ExportArmatures:
+ #Create a copy of the object and remove all armature modifiers so an unshaped
+ #mesh can be created from it.
+ Object2 = Object.copy()
+ for Modifier in [Modifier for Modifier in Object2.modifiers if Modifier.type == "ARMATURE"]:
+ Object2.modifiers.remove(Modifier)
+ Mesh = Object2.create_mesh(bpy.context.scene, True, "PREVIEW")
+ else:
+ Mesh = Object.create_mesh(bpy.context.scene, True, "PREVIEW")
+ else:
+ Mesh = Object.create_mesh(bpy.context.scene, False, "PREVIEW")
+ if Config.Verbose:
+ print(" Done")
+ print(" Writing Mesh...")
+ WriteMesh(Config, Object, Mesh)
+ if Config.Verbose:
+ print(" Done")
+ if Config.ApplyModifiers and Config.ExportArmatures:
+ bpy.data.objects.remove(Object2)
+ bpy.data.meshes.remove(Mesh)
+
+ Config.Whitespace -= 1
+ Config.File.write("{}}} //End of {}\n".format(" " * Config.Whitespace, LegalName(Object.name)))
+ if Config.Verbose:
+ print(" Done Writing Object: {}".format(Object.name))
+
+
+def WriteLocalMatrix(Config, Object):
+ LocalMatrix = Object.matrix_local
+
+ Config.File.write("{}FrameTransformMatrix {{\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format(" " * Config.Whitespace, LocalMatrix[0][0], LocalMatrix[0][1], LocalMatrix[0][2], LocalMatrix[0][3]))
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format(" " * Config.Whitespace, LocalMatrix[1][0], LocalMatrix[1][1], LocalMatrix[1][2], LocalMatrix[1][3]))
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format(" " * Config.Whitespace, LocalMatrix[2][0], LocalMatrix[2][1], LocalMatrix[2][2], LocalMatrix[2][3]))
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f};;\n".format(" " * Config.Whitespace, LocalMatrix[3][0], LocalMatrix[3][1], LocalMatrix[3][2], LocalMatrix[3][3]))
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+
+
+def WriteArmatureBones(Config, Object, ChildList):
+ PoseBones = Object.pose.bones
+ for Bone in ChildList:
+ if Config.Verbose:
+ print(" Writing Bone: {}...".format(Bone.name))
+ Config.File.write("{}Frame {} {{\n".format(" " * Config.Whitespace, LegalName(Object.name) + "_" + LegalName(Bone.name)))
+ Config.Whitespace += 1
+
+ PoseBone = PoseBones[Bone.name]
+
+ if Bone.parent:
+ BoneMatrix = PoseBone.parent.matrix.copy().invert()
+ else:
+ BoneMatrix = Matrix()
+
+ BoneMatrix *= PoseBone.matrix
+
+ Config.File.write("{}FrameTransformMatrix {{\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format(" " * Config.Whitespace, BoneMatrix[0][0], BoneMatrix[0][1], BoneMatrix[0][2], BoneMatrix[0][3]))
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format(" " * Config.Whitespace, BoneMatrix[1][0], BoneMatrix[1][1], BoneMatrix[1][2], BoneMatrix[1][3]))
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format(" " * Config.Whitespace, BoneMatrix[2][0], BoneMatrix[2][1], BoneMatrix[2][2], BoneMatrix[2][3]))
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f};;\n".format(" " * Config.Whitespace, BoneMatrix[3][0], BoneMatrix[3][1], BoneMatrix[3][2], BoneMatrix[3][3]))
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+
+ if Config.Verbose:
+ print(" Done")
+ WriteArmatureBones(Config, Object, Bone.children)
+ Config.Whitespace -= 1
+
+ Config.File.write("{}}} //End of {}\n".format(" " * Config.Whitespace, LegalName(Object.name) + "_" + LegalName(Bone.name)))
+
+
+def WriteMesh(Config, Object, Mesh):
+ Config.File.write("{}Mesh {{ //{} Mesh\n".format(" " * Config.Whitespace, LegalName(Mesh.name)))
+ Config.Whitespace += 1
+
+ if Config.Verbose:
+ print(" Writing Mesh Vertices...")
+ WriteMeshVertices(Config, Mesh)
+ if Config.Verbose:
+ print(" Done\n Writing Mesh Normals...")
+ WriteMeshNormals(Config, Mesh)
+ if Config.Verbose:
+ print(" Done\n Writing Mesh Materials...")
+ WriteMeshMaterials(Config, Mesh)
+ if Config.Verbose:
+ print(" Done")
+ if Mesh.uv_textures:
+ if Config.Verbose:
+ print(" Writing Mesh UV Coordinates...")
+ WriteMeshUVCoordinates(Config, Mesh)
+ if Config.Verbose:
+ print(" Done")
+ if Config.ExportArmatures:
+ if Config.Verbose:
+ print(" Writing Mesh Skin Weights...")
+ WriteMeshSkinWeights(Config, Object, Mesh)
+ if Config.Verbose:
+ print(" Done")
+
+ Config.Whitespace -= 1
+ Config.File.write("{}}} //End of {} Mesh\n".format(" " * Config.Whitespace, LegalName(Mesh.name)))
+
+
+def WriteMeshVertices(Config, Mesh):
+ Index = 0
+ VertexCount = GetMeshVertexCount(Mesh)
+ Config.File.write("{}{};\n".format(" " * Config.Whitespace, VertexCount))
+
+ for Face in Mesh.faces:
+ Vertices = list(Face.vertices)
+
+ if Config.CoordinateSystem == 1:
+ Vertices = Vertices[::-1]
+ for Vertex in [Mesh.vertices[Vertex] for Vertex in Vertices]:
+ Position = Vertex.co
+ Config.File.write("{}{:9f};{:9f};{:9f};".format(" " * Config.Whitespace, Position[0], Position[1], Position[2]))
+ Index += 1
+ if Index == VertexCount:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+
+ Index = 0
+ Config.File.write("{}{};\n".format(" " * Config.Whitespace, len(Mesh.faces)))
+
+ for Face in Mesh.faces:
+ Config.File.write("{}{};".format(" " * Config.Whitespace, len(Face.vertices)))
+ for Vertex in Face.vertices:
+ Config.File.write("{};".format(Index))
+ Index += 1
+ if Index == VertexCount:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+
+
+def WriteMeshNormals(Config, Mesh):
+ Config.File.write("{}MeshNormals {{ //{} Normals\n".format(" " * Config.Whitespace, LegalName(Mesh.name)))
+ Config.Whitespace += 1
+
+ Index = 0
+ VertexCount = GetMeshVertexCount(Mesh)
+ Config.File.write("{}{};\n".format(" " * Config.Whitespace, VertexCount))
+
+ for Face in Mesh.faces:
+ Vertices = list(Face.vertices)
+
+ if Config.CoordinateSystem == 1:
+ Vertices = Vertices[::-1]
+ for Vertex in [Mesh.vertices[Vertex] for Vertex in Vertices]:
+ if Face.use_smooth:
+ Normal = Vertex.normal
+ else:
+ Normal = Face.normal
+ if Config.FlipNormals:
+ Normal = -Normal
+ Config.File.write("{}{:9f};{:9f};{:9f};".format(" " * Config.Whitespace, Normal[0], Normal[1], Normal[2]))
+ Index += 1
+ if Index == VertexCount:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+
+ Index = 0
+ Config.File.write("{}{};\n".format(" " * Config.Whitespace, len(Mesh.faces)))
+
+ for Face in Mesh.faces:
+ Config.File.write("{}{};".format(" " * Config.Whitespace, len(Face.vertices)))
+ for Vertex in Face.vertices:
+ Config.File.write("{};".format(Index))
+ Index += 1
+ if Index == VertexCount:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+ Config.Whitespace -= 1
+ Config.File.write("{}}} //End of {} Normals\n".format(" " * Config.Whitespace, LegalName(Mesh.name)))
+
+
+def WriteMeshMaterials(Config, Mesh):
+ Config.File.write("{}MeshMaterialList {{ //{} Material List\n".format(" " * Config.Whitespace, LegalName(Mesh.name)))
+ Config.Whitespace += 1
+
+ Materials = Mesh.materials
+ if Materials.keys():
+ MaterialIndexes = {}
+ for Face in Mesh.faces:
+ if Materials[Face.material_index] not in MaterialIndexes:
+ MaterialIndexes[Materials[Face.material_index]] = len(MaterialIndexes)
+
+ FaceCount = len(Mesh.faces)
+ Index = 0
+ Config.File.write("{}{};\n{}{};\n".format(" " * Config.Whitespace, len(MaterialIndexes), " " * Config.Whitespace, FaceCount))
+ for Face in Mesh.faces:
+ Config.File.write("{}{}".format(" " * Config.Whitespace, MaterialIndexes[Materials[Face.material_index]]))
+ Index += 1
+ if Index == FaceCount:
+ Config.File.write(";;\n")
+ else:
+ Config.File.write(",\n")
+
+ Materials = [Item[::-1] for Item in MaterialIndexes.items()]
+ Materials.sort()
+ for Material in Materials:
+ WriteMaterial(Config, Material[1])
+ else:
+ Config.File.write("{}1;\n{}1;\n{}0;;\n".format(" " * Config.Whitespace, " " * Config.Whitespace, " " * Config.Whitespace))
+ WriteMaterial(Config)
+
+ Config.Whitespace -= 1
+ Config.File.write("{}}} //End of {} Material List\n".format(" " * Config.Whitespace, LegalName(Mesh.name)))
+
+
+def WriteMaterial(Config, Material=None):
+ if Material:
+ Config.File.write("{}Material {} {{\n".format(" " * Config.Whitespace, LegalName(Material.name)))
+ Config.Whitespace += 1
+
+ Diffuse = list(Material.diffuse_color)
+ Diffuse.append(Material.alpha)
+ Specularity = Material.specular_intensity
+ Specular = list(Material.specular_color)
+
+ Config.File.write("{}{:9f};{:9f};{:9f};{:9f};;\n".format(" " * Config.Whitespace, Diffuse[0], Diffuse[1], Diffuse[2], Diffuse[3]))
+ Config.File.write("{}{:9f};\n".format(" " * Config.Whitespace, 2 * (1.0 - Specularity)))
+ Config.File.write("{}{:9f};{:9f};{:9f};;\n".format(" " * Config.Whitespace, Specular[0], Specular[1], Specular[2]))
+ else:
+ Config.File.write("{}Material Default_Material {{\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{} 1.000000; 1.000000; 1.000000; 1.000000;;\n".format(" " * Config.Whitespace))
+ Config.File.write("{} 1.500000;\n".format(" " * Config.Whitespace))
+ Config.File.write("{} 1.000000; 1.000000; 1.000000;;\n".format(" " * Config.Whitespace))
+ Config.File.write("{} 0.000000; 0.000000; 0.000000;;\n".format(" " * Config.Whitespace))
+ if Config.ExportTextures:
+ Texture = GetMaterialTexture(Material)
+ if Texture:
+ Config.File.write("{}TextureFilename {{\"{}\";}}\n".format(" " * Config.Whitespace, Texture))
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+
+
+def WriteMeshUVCoordinates(Config, Mesh):
+ Config.File.write("{}MeshTextureCoords {{ //{} UV Coordinates\n".format(" " * Config.Whitespace, LegalName(Mesh.name)))
+ Config.Whitespace += 1
+
+ UVCoordinates = None
+ for UV in Mesh.uv_textures:
+ if UV.active_render:
+ UVCoordinates = UV.data
+ break
+
+ Index = 0
+ VertexCount = GetMeshVertexCount(Mesh)
+ Config.File.write("{}{};\n".format(" " * Config.Whitespace, VertexCount))
+
+ for Face in UVCoordinates:
+ Vertices = []
+ for Vertex in Face.uv:
+ Vertices.append(tuple(Vertex))
+ if Config.CoordinateSystem == 1:
+ Vertices = Vertices[::-1]
+ for Vertex in Vertices:
+ Config.File.write("{}{:9f};{:9f};".format(" " * Config.Whitespace, Vertex[0], 1 - Vertex[1]))
+ Index += 1
+ if Index == VertexCount:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+ Config.Whitespace -= 1
+ Config.File.write("{}}} //End of {} UV Coordinates\n".format(" " * Config.Whitespace, LegalName(Mesh.name)))
+
+
+def WriteMeshSkinWeights(Config, Object, Mesh):
+ ArmatureList = [Modifier for Modifier in Object.modifiers if Modifier.type == "ARMATURE"]
+ if ArmatureList:
+ ArmatureObject = ArmatureList[0].object
+ ArmatureBones = ArmatureObject.data.bones
+
+ PoseBones = ArmatureObject.pose.bones
+
+ MaxInfluences = 0
+ UsedBones = set()
+ #Maps bones to a list of vertices they affect
+ VertexGroups = {}
+
+ for Vertex in Mesh.vertices:
+ #BoneInfluences contains the bones of the armature that affect the current vertex
+ BoneInfluences = [PoseBones[Object.vertex_groups[Group.group].name] for Group in Vertex.groups if Object.vertex_groups[Group.group].name in PoseBones]
+ if len(BoneInfluences) > MaxInfluences:
+ MaxInfluences = len(BoneInfluences)
+ for Bone in BoneInfluences:
+ UsedBones.add(Bone)
+ if Bone not in VertexGroups:
+ VertexGroups[Bone] = [Vertex]
+ else:
+ VertexGroups[Bone].append(Vertex)
+ BoneCount = len(UsedBones)
+
+ Config.File.write("{}XSkinMeshHeader {{\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{}{};\n{}{};\n{}{};\n".format(" " * Config.Whitespace, MaxInfluences, " " * Config.Whitespace, MaxInfluences * 3, " " * Config.Whitespace, BoneCount))
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+
+ for Bone in UsedBones:
+ VertexCount = 0
+ VertexIndexes = [Vertex.index for Vertex in VertexGroups[Bone]]
+ for Face in Mesh.faces:
+ for Vertex in Face.vertices:
+ if Vertex in VertexIndexes:
+ VertexCount += 1
+
+ Config.File.write("{}SkinWeights {{\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{}\"{}\";\n{}{};\n".format(" " * Config.Whitespace, LegalName(ArmatureObject.name) + "_" + LegalName(Bone.name), " " * Config.Whitespace, VertexCount))
+
+ VertexWeights = []
+ Index = 0
+ WrittenIndexes = 0
+ for Face in Mesh.faces:
+ FaceVertices = list(Face.vertices)
+ if Config.CoordinateSystem == 1:
+ FaceVertices = FaceVertices[::-1]
+ for Vertex in FaceVertices:
+ if Vertex in VertexIndexes:
+ Config.File.write("{}{}".format(" " * Config.Whitespace, Index))
+
+ GroupIndexes = {Object.vertex_groups[Group.group].name: Index for Index, Group in enumerate(Mesh.vertices[Vertex].groups) if Object.vertex_groups[Group.group].name in PoseBones}
+
+ WeightTotal = 0.0
+ for Weight in [Group.weight for Group in Mesh.vertices[Vertex].groups if Object.vertex_groups[Group.group].name in PoseBones]:
+ WeightTotal += Weight
+
+ if WeightTotal:
+ VertexWeights.append(Mesh.vertices[Vertex].groups[GroupIndexes[Bone.name]].weight / WeightTotal)
+ else:
+ VertexWeights.append(0.0)
+
+ WrittenIndexes += 1
+ if WrittenIndexes == VertexCount:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+ Index += 1
+
+ for Index, Weight in enumerate(VertexWeights):
+ Config.File.write("{}{:8f}".format(" " * Config.Whitespace, Weight))
+ if Index == (VertexCount - 1):
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+
+ RestBone = ArmatureBones[Bone.name]
+
+ #BoneMatrix transforms mesh vertices into the space of the bone.
+ #Here are the final transformations in order:
+ # - Object Space to World Space
+ # - World Space to Armature Space
+ # - Armature Space to Bone Space (The bone matrix needs to be rotated 90 degrees to align with Blender's world axes)
+ #This way, when BoneMatrix is transformed by the bone's Frame matrix, the vertices will be in their final world position.
+
+ BoneMatrix = RestBone.matrix_local.copy().invert()
+ BoneMatrix *= ArmatureObject.matrix_world.copy().invert()
+ BoneMatrix *= Object.matrix_world
+
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format(" " * Config.Whitespace, BoneMatrix[0][0], BoneMatrix[0][1], BoneMatrix[0][2], BoneMatrix[0][3]))
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format(" " * Config.Whitespace, BoneMatrix[1][0], BoneMatrix[1][1], BoneMatrix[1][2], BoneMatrix[1][3]))
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f},\n".format(" " * Config.Whitespace, BoneMatrix[2][0], BoneMatrix[2][1], BoneMatrix[2][2], BoneMatrix[2][3]))
+ Config.File.write("{}{:9f},{:9f},{:9f},{:9f};;\n".format(" " * Config.Whitespace, BoneMatrix[3][0], BoneMatrix[3][1], BoneMatrix[3][2], BoneMatrix[3][3]))
+ Config.Whitespace -= 1
+ Config.File.write("{}}} //End of {} Skin Weights\n".format(" " * Config.Whitespace, LegalName(ArmatureObject.name) + "_" + LegalName(Bone.name)))
+
+
+def WriteKeyedAnimationSet(Config):
+ Config.File.write("{}AnimationSet {{\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ 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:
+ PositionFCurves = [None, None, None]
+ RotationFCurves = [None, None, None]
+ ScaleFCurves = [None, None, None]
+ for FCurve in Action.fcurves:
+ if FCurve.data_path == "location":
+ PositionFCurves[FCurve.array_index] = FCurve
+ elif FCurve.data_path == "rotation_euler":
+ RotationFCurves[FCurve.array_index] = FCurve
+ elif FCurve.data_path == "scale":
+ ScaleFCurves[FCurve.array_index] = FCurve
+ if [FCurve for FCurve in PositionFCurves + RotationFCurves + ScaleFCurves if FCurve]:
+ Config.File.write("{}Animation {{\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{}{{{}}}\n".format(" " * Config.Whitespace, LegalName(Object.name)))
+
+ #Position
+ if Config.Verbose:
+ print(" Writing Position...")
+ AllKeyframes = set()
+ for Index, FCurve in enumerate(PositionFCurves):
+ if FCurve:
+ Keyframes = []
+ for Keyframe in FCurve.keyframe_points:
+ if Keyframe.co[0] < bpy.context.scene.frame_start:
+ AllKeyframes.add(bpy.context.scene.frame_start)
+ elif Keyframe.co[0] > bpy.context.scene.frame_end:
+ AllKeyframes.add(bpy.context.scene.frame_end)
+ else:
+ Keyframes.append(Keyframe.co)
+ AllKeyframes.add(int(Keyframe.co[0]))
+ PositionFCurves[Index] = {int(Keyframe): Value for Keyframe, Value in Keyframes}
+ Config.File.write("{}AnimationKey {{ //Position\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ AllKeyframes = list(AllKeyframes)
+ AllKeyframes.sort()
+ if len(AllKeyframes):
+ Config.File.write("{}2;\n{}{};\n".format(" " * Config.Whitespace, " " * Config.Whitespace, len(AllKeyframes)))
+ for Keyframe in AllKeyframes:
+ bpy.context.scene.frame_set(Keyframe)
+ Position = Vector()
+ Position[0] = ((PositionFCurves[0][Keyframe] if Keyframe in PositionFCurves[0] else Object.location[0]) if PositionFCurves[0] else Object.location[0])
+ Position[1] = ((PositionFCurves[1][Keyframe] if Keyframe in PositionFCurves[1] else Object.location[1]) if PositionFCurves[1] else Object.location[1])
+ Position[2] = ((PositionFCurves[2][Keyframe] if Keyframe in PositionFCurves[2] else Object.location[2]) if PositionFCurves[2] else Object.location[2])
+ 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]))
+ if Keyframe == AllKeyframes[-1]:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+
+ else:
+ Config.File.write("{}2;\n{}1;\n".format(" " * Config.Whitespace, " " * Config.Whitespace))
+ bpy.context.scene.frame_set(bpy.context.scene.frame_start)
+ Position = Object.matrix_local.translation_part()
+ Config.File.write("{}{}{:9f},{:9f},{:9f};;;\n".format(" " * Config.Whitespace, ("0;3;").ljust(8), Position[0], Position[1], Position[2]))
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ if Config.Verbose:
+ print(" Done")
+
+ #Rotation
+ if Config.Verbose:
+ print(" Writing Rotation...")
+ AllKeyframes = set()
+ for Index, FCurve in enumerate(RotationFCurves):
+ if FCurve:
+ Keyframes = []
+ for Keyframe in FCurve.keyframe_points:
+ if Keyframe.co[0] < bpy.context.scene.frame_start:
+ AllKeyframes.add(bpy.context.scene.frame_start)
+ elif Keyframe.co[0] > bpy.context.scene.frame_end:
+ AllKeyframes.add(bpy.context.scene.frame_end)
+ else:
+ Keyframes.append(Keyframe.co)
+ AllKeyframes.add(int(Keyframe.co[0]))
+ RotationFCurves[Index] = {int(Keyframe): Value for Keyframe, Value in Keyframes}
+ Config.File.write("{}AnimationKey {{ //Rotation\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ AllKeyframes = list(AllKeyframes)
+ AllKeyframes.sort()
+ if len(AllKeyframes):
+ Config.File.write("{}0;\n{}{};\n".format(" " * Config.Whitespace, " " * Config.Whitespace, len(AllKeyframes)))
+ for Keyframe in AllKeyframes:
+ bpy.context.scene.frame_set(Keyframe)
+ Rotation = Euler()
+ Rotation[0] = ((RotationFCurves[0][Keyframe] if Keyframe in RotationFCurves[0] else Object.rotation_euler[0]) if RotationFCurves[0] else Object.rotation_euler[0])
+ Rotation[1] = ((RotationFCurves[1][Keyframe] if Keyframe in RotationFCurves[1] else Object.rotation_euler[1]) if RotationFCurves[1] else Object.rotation_euler[1])
+ Rotation[2] = ((RotationFCurves[2][Keyframe] if Keyframe in RotationFCurves[2] else Object.rotation_euler[2]) if RotationFCurves[2] else Object.rotation_euler[2])
+ Rotation = Rotation.to_quat()
+ 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]))
+ if Keyframe == AllKeyframes[-1]:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+ else:
+ Config.File.write("{}0;\n{}1;\n".format(" " * Config.Whitespace, " " * Config.Whitespace))
+ bpy.context.scene.frame_set(bpy.context.scene.frame_start)
+ Rotation = Object.rotation_euler.to_quat()
+ Config.File.write("{}{}{:9f},{:9f},{:9f},{:9f};;;\n".format(" " * Config.Whitespace, ("0;4;").ljust(8), -Rotation[0], Rotation[1], Rotation[2], Rotation[3]))
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ if Config.Verbose:
+ print(" Done")
+
+ #Scale
+ if Config.Verbose:
+ print(" Writing Scale...")
+ AllKeyframes = set()
+ for Index, FCurve in enumerate(ScaleFCurves):
+ if FCurve:
+ Keyframes = []
+ for Keyframe in FCurve.keyframe_points:
+ if Keyframe.co[0] < bpy.context.scene.frame_start:
+ AllKeyframes.add(bpy.context.scene.frame_start)
+ elif Keyframe.co[0] > bpy.context.scene.frame_end:
+ AllKeyframes.add(bpy.context.scene.frame_end)
+ else:
+ Keyframes.append(Keyframe.co)
+ AllKeyframes.add(int(Keyframe.co[0]))
+ ScaleFCurves[Index] = {int(Keyframe): Value for Keyframe, Value in Keyframes}
+ Config.File.write("{}AnimationKey {{ //Scale\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ AllKeyframes = list(AllKeyframes)
+ AllKeyframes.sort()
+ if len(AllKeyframes):
+ Config.File.write("{}1;\n{}{};\n".format(" " * Config.Whitespace, " " * Config.Whitespace, len(AllKeyframes)))
+ for Keyframe in AllKeyframes:
+ bpy.context.scene.frame_set(Keyframe)
+ Scale = Vector()
+ Scale[0] = ((ScaleFCurves[0][Keyframe] if Keyframe in ScaleFCurves[0] else Object.scale[0]) if ScaleFCurves[0] else Object.scale[0])
+ Scale[1] = ((ScaleFCurves[1][Keyframe] if Keyframe in ScaleFCurves[1] else Object.scale[1]) if ScaleFCurves[1] else Object.scale[1])
+ Scale[2] = ((ScaleFCurves[2][Keyframe] if Keyframe in ScaleFCurves[2] else Object.scale[2]) if ScaleFCurves[2] else Object.scale[2])
+ 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]))
+ if Keyframe == AllKeyframes[-1]:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+ else:
+ Config.File.write("{}1;\n{}1;\n".format(" " * Config.Whitespace, " " * Config.Whitespace))
+ bpy.context.scene.frame_set(bpy.context.scene.frame_start)
+ Scale = Object.matrix_local.scale_part()
+ Config.File.write("{}{}{:9f},{:9f},{:9f};;;\n".format(" " * Config.Whitespace, ("0;3;").ljust(8), Scale[0], Scale[1], Scale[2]))
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ if Config.Verbose:
+ print(" Done")
+
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ else:
+ if Config.Verbose:
+ print(" Object has no useable animation data.")
+
+ if Config.ExportArmatures and Object.type == "ARMATURE":
+ if Config.Verbose:
+ print(" Writing Armature Bone Animation Data...")
+ PoseBones = Object.pose.bones
+ for Bone in PoseBones:
+ if Config.Verbose:
+ print(" Writing Bone: {}...".format(Bone.name))
+ PositionFCurves = [None, None, None]
+ RotationFCurves = [None, None, None, None]
+ ScaleFCurves = [None, None, None]
+ for FCurve in Action.fcurves:
+ if FCurve.data_path == "pose.bones[\"{}\"].location".format(Bone.name):
+ PositionFCurves[FCurve.array_index] = FCurve
+ elif FCurve.data_path == "pose.bones[\"{}\"].rotation_quaternion".format(Bone.name):
+ RotationFCurves[FCurve.array_index] = FCurve
+ elif FCurve.data_path == "pose.bones[\"{}\"].scale".format(Bone.name):
+ ScaleFCurves[FCurve.array_index] = FCurve
+ if not [FCurve for FCurve in PositionFCurves + RotationFCurves + ScaleFCurves if FCurve]:
+ if Config.Verbose:
+ print(" Bone has no useable animation data.\n Done")
+ continue
+
+ Config.File.write("{}Animation {{\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{}{{{}}}\n".format(" " * Config.Whitespace, LegalName(Object.name) + "_" + LegalName(Bone.name)))
+
+ #Position
+ if Config.Verbose:
+ print(" Writing Position...")
+ AllKeyframes = set()
+ for Index, FCurve in enumerate(PositionFCurves):
+ if FCurve:
+ Keyframes = []
+ for Keyframe in FCurve.keyframe_points:
+ if Keyframe.co[0] < bpy.context.scene.frame_start:
+ AllKeyframes.add(bpy.context.scene.frame_start)
+ elif Keyframe.co[0] > bpy.context.scene.frame_end:
+ AllKeyframes.add(bpy.context.scene.frame_end)
+ else:
+ Keyframes.append(Keyframe.co)
+ AllKeyframes.add(int(Keyframe.co[0]))
+ PositionFCurves[Index] = {int(Keyframe): Value for Keyframe, Value in Keyframes}
+ Config.File.write("{}AnimationKey {{ //Position\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ AllKeyframes = list(AllKeyframes)
+ AllKeyframes.sort()
+ if not len(AllKeyframes):
+ AllKeyframes = [bpy.context.scene.frame_start]
+ Config.File.write("{}2;\n{}{};\n".format(" " * Config.Whitespace, " " * Config.Whitespace, len(AllKeyframes)))
+ for Keyframe in AllKeyframes:
+ bpy.context.scene.frame_set(Keyframe)
+
+ if Bone.parent:
+ PoseMatrix = Bone.parent.matrix.copy().invert()
+ else:
+ PoseMatrix = Matrix()
+ PoseMatrix *= Bone.matrix
+
+ Position = PoseMatrix.translation_part()
+ 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]))
+ if Keyframe == AllKeyframes[-1]:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ if Config.Verbose:
+ print(" Done")
+
+ #Rotation
+ if Config.Verbose:
+ print(" Writing Rotation...")
+ AllKeyframes = set()
+ for Index, FCurve in enumerate(RotationFCurves):
+ if FCurve:
+ Keyframes = []
+ for Keyframe in FCurve.keyframe_points:
+ if Keyframe.co[0] < bpy.context.scene.frame_start:
+ AllKeyframes.add(bpy.context.scene.frame_start)
+ elif Keyframe.co[0] > bpy.context.scene.frame_end:
+ AllKeyframes.add(bpy.context.scene.frame_end)
+ else:
+ Keyframes.append(Keyframe.co)
+ AllKeyframes.add(int(Keyframe.co[0]))
+ RotationFCurves[Index] = {int(Keyframe): Value for Keyframe, Value in Keyframes}
+ Config.File.write("{}AnimationKey {{ //Rotation\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ AllKeyframes = list(AllKeyframes)
+ AllKeyframes.sort()
+ if not len(AllKeyframes):
+ AllKeyframes = [bpy.context.scene.frame_start]
+ Config.File.write("{}0;\n{}{};\n".format(" " * Config.Whitespace, " " * Config.Whitespace, len(AllKeyframes)))
+ for Keyframe in AllKeyframes:
+ bpy.context.scene.frame_set(Keyframe)
+
+ if Bone.parent:
+ PoseMatrix = Bone.parent.matrix.copy().invert()
+ else:
+ PoseMatrix = Matrix()
+ PoseMatrix *= Bone.matrix
+
+ Rotation = PoseMatrix.rotation_part().to_quat()
+ 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]))
+ if Keyframe == AllKeyframes[-1]:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ if Config.Verbose:
+ print(" Done")
+
+ #Scale
+ if Config.Verbose:
+ print(" Writing Scale...")
+ AllKeyframes = set()
+ for Index, FCurve in enumerate(ScaleFCurves):
+ if FCurve:
+ Keyframes = []
+ for Keyframe in FCurve.keyframe_points:
+ if Keyframe.co[0] < bpy.context.scene.frame_start:
+ AllKeyframes.add(bpy.context.scene.frame_start)
+ elif Keyframe.co[0] > bpy.context.scene.frame_end:
+ AllKeyframes.add(bpy.context.scene.frame_end)
+ else:
+ Keyframes.append(Keyframe.co)
+ AllKeyframes.add(int(Keyframe.co[0]))
+ ScaleFCurves[Index] = {int(Keyframe): Value for Keyframe, Value in Keyframes}
+ Config.File.write("{}AnimationKey {{ //Scale\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ AllKeyframes = list(AllKeyframes)
+ AllKeyframes.sort()
+ if not len(AllKeyframes):
+ AllKeyframes = [bpy.context.scene.frame_start]
+ Config.File.write("{}1;\n{}{};\n".format(" " * Config.Whitespace, " " * Config.Whitespace, len(AllKeyframes)))
+ for Keyframe in AllKeyframes:
+ bpy.context.scene.frame_set(Keyframe)
+
+ if Bone.parent:
+ PoseMatrix = Bone.parent.matrix.copy().invert()
+ else:
+ PoseMatrix = Matrix()
+ PoseMatrix *= Bone.matrix
+
+ Scale = PoseMatrix.scale_part()
+ 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]))
+ if Keyframe == AllKeyframes[-1]:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ if Config.Verbose:
+ print(" Done")
+
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ if Config.Verbose:
+ print(" Done") #Done with Armature Bone
+ if Config.Verbose:
+ print(" Done") #Done with Armature Bone data
+ if Config.Verbose:
+ print(" Done") #Done with Object
+
+ Config.Whitespace -= 1
+ Config.File.write("{}}} //End of AnimationSet\n".format(" " * Config.Whitespace))
+
+def WriteFullAnimationSet(Config):
+ Config.File.write("{}AnimationSet {{\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+
+ KeyframeCount = bpy.context.scene.frame_end - bpy.context.scene.frame_start + 1
+
+ for Object in Config.ObjectList:
+ if Config.Verbose:
+ print(" Writing Animation Data for Object: {}".format(Object.name))
+
+ Config.File.write("{}Animation {{\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{}{{{}}}\n".format(" " * Config.Whitespace, LegalName(Object.name)))
+
+ #Position
+ if Config.Verbose:
+ print(" Writing Position...")
+ Config.File.write("{}AnimationKey {{ //Position\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{}2;\n{}{};\n".format(" " * Config.Whitespace, " " * Config.Whitespace, KeyframeCount))
+ for Frame in range(0, KeyframeCount):
+ bpy.context.scene.frame_set(Frame + bpy.context.scene.frame_start)
+ Position = Object.matrix_local.translation_part()
+ Config.File.write("{}{}{:9f},{:9f},{:9f};;".format(" " * Config.Whitespace, (str(Frame) + ";3;").ljust(8), Position[0], Position[1], Position[2]))
+ if Frame == KeyframeCount-1:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ if Config.Verbose:
+ print(" Done")
+
+ #Rotation
+ if Config.Verbose:
+ print(" Writing Rotation...")
+ Config.File.write("{}AnimationKey {{ //Rotation\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{}0;\n{}{};\n".format(" " * Config.Whitespace, " " * Config.Whitespace, KeyframeCount))
+ for Frame in range(0, KeyframeCount):
+ bpy.context.scene.frame_set(Frame + bpy.context.scene.frame_start)
+ Rotation = Object.rotation_euler.to_quat()
+ Config.File.write("{}{}{:9f},{:9f},{:9f},{:9f};;".format(" " * Config.Whitespace, (str(Frame) + ";4;").ljust(8), -Rotation[0], Rotation[1], Rotation[2], Rotation[3]))
+ if Frame == KeyframeCount-1:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ if Config.Verbose:
+ print(" Done")
+
+ #Scale
+ if Config.Verbose:
+ print(" Writing Scale...")
+ Config.File.write("{}AnimationKey {{ //Scale\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{}1;\n{}{};\n".format(" " * Config.Whitespace, " " * Config.Whitespace, KeyframeCount))
+ for Frame in range(0, KeyframeCount):
+ bpy.context.scene.frame_set(Frame + bpy.context.scene.frame_start)
+ Scale = Object.matrix_local.scale_part()
+ Config.File.write("{}{}{:9f},{:9f},{:9f};;".format(" " * Config.Whitespace, (str(Frame) + ";3;").ljust(8), Scale[0], Scale[1], Scale[2]))
+ if Frame == KeyframeCount-1:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ if Config.Verbose:
+ print(" Done")
+
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+
+ if Config.ExportArmatures and Object.type == "ARMATURE":
+ if Config.Verbose:
+ print(" Writing Armature Bone Animation Data...")
+ PoseBones = Object.pose.bones
+ Bones = Object.data.bones
+ for Bone in PoseBones:
+ if Config.Verbose:
+ print(" Writing Bone: {}...".format(Bone.name))
+
+ Config.File.write("{}Animation {{\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{}{{{}}}\n".format(" " * Config.Whitespace, LegalName(Object.name) + "_" + LegalName(Bone.name)))
+
+ #Position
+ if Config.Verbose:
+ print(" Writing Position...")
+ Config.File.write("{}AnimationKey {{ //Position\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{}2;\n{}{};\n".format(" " * Config.Whitespace, " " * Config.Whitespace, KeyframeCount))
+ for Frame in range(0, KeyframeCount):
+ bpy.context.scene.frame_set(Frame + bpy.context.scene.frame_start)
+
+ if Bone.parent:
+ PoseMatrix = Bone.parent.matrix.copy().invert()
+ else:
+ PoseMatrix = Matrix()
+ PoseMatrix *= Bone.matrix
+
+ Position = PoseMatrix.translation_part()
+ Config.File.write("{}{}{:9f},{:9f},{:9f};;".format(" " * Config.Whitespace, (str(Frame) + ";3;").ljust(8), Position[0], Position[1], Position[2]))
+ if Frame == KeyframeCount-1:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ if Config.Verbose:
+ print(" Done")
+
+ #Rotation
+ if Config.Verbose:
+ print(" Writing Rotation...")
+ Config.File.write("{}AnimationKey {{ //Rotation\n".format(" " * Config.Whitespace))
+ Config.Whitespace += 1
+ Config.File.write("{}0;\n{}{};\n".format(" " * Config.Whitespace, " " * Config.Whitespace, KeyframeCount))
+ for Frame in range(0, KeyframeCount):
+ bpy.context.scene.frame_set(Frame + bpy.context.scene.frame_start)
+
+ Rotation = Bones[Bone.name].matrix.to_quat() * Bone.rotation_quaternion
+
+ Config.File.write("{}{}{:9f},{:9f},{:9f},{:9f};;".format(" " * Config.Whitespace, (str(Frame) + ";4;").ljust(8), -Rotation[0], Rotation[1], Rotation[2], Rotation[3]))
+ if Frame == KeyframeCount-1:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ if Config.Verbose:
+ print(" Done")
+
+ #Scale
+ if Config.Verbose:
+ print(" Writing Scale...")
+ Config.File.write("{}AnimationKey {{ //Scale\n".format(" " * Config.Whitespace, KeyframeCount))
+ Config.Whitespace += 1
+ Config.File.write("{}1;\n{}{};\n".format(" " * Config.Whitespace, " " * Config.Whitespace, KeyframeCount))
+ for Frame in range(0, KeyframeCount):
+ bpy.context.scene.frame_set(Frame + bpy.context.scene.frame_start)
+
+ if Bone.parent:
+ PoseMatrix = Bone.parent.matrix.copy().invert()
+ else:
+ PoseMatrix = Matrix()
+ PoseMatrix *= Bone.matrix
+
+ Scale = PoseMatrix.scale_part()
+ Config.File.write("{}{}{:9f},{:9f},{:9f};;".format(" " * Config.Whitespace, (str(Frame) + ";3;").ljust(8), Scale[0], Scale[1], Scale[2]))
+ if Frame == KeyframeCount-1:
+ Config.File.write(";\n")
+ else:
+ Config.File.write(",\n")
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ if Config.Verbose:
+ print(" Done")
+
+ Config.Whitespace -= 1
+ Config.File.write("{}}}\n".format(" " * Config.Whitespace))
+ if Config.Verbose:
+ print(" Done") #Done with Armature Bone
+ if Config.Verbose:
+ print(" Done") #Done with Armature Bone data
+ if Config.Verbose:
+ print(" Done") #Done with Object
+
+ Config.Whitespace -= 1
+ Config.File.write("{}}} //End of AnimationSet\n".format(" " * Config.Whitespace))
+
+
+def CloseFile(Config):
+ if Config.Verbose:
+ print("Closing File...")
+ Config.File.close()
+ if Config.Verbose:
+ print("Done")
+
+CoordinateSystems = []
+CoordinateSystems.append(("1", "Left-Handed", ""))
+CoordinateSystems.append(("2", "Right-Handed", ""))
+
+AnimationModes = []
+AnimationModes.append(("0", "None", ""))
+AnimationModes.append(("1", "Keyframes Only", ""))
+AnimationModes.append(("2", "Full Animation", ""))
+
+ExportModes = []
+ExportModes.append(("1", "All Objects", ""))
+ExportModes.append(("2", "Selected Objects", ""))
+
+from bpy.props import *
+
+
+class DirectXExporter(bpy.types.Operator):
+ """Export to the DirectX model format (.x)"""
+
+ bl_idname = "export.directx"
+ bl_label = "Export DirectX"
+
+ filepath = StringProperty(subtype='FILE_PATH')
+
+ #Coordinate System
+ CoordinateSystem = EnumProperty(name="System", description="Select a coordinate system to export to", items=CoordinateSystems, default="1")
+
+ #General Options
+ RotateX = BoolProperty(name="Rotate X 90 Degrees", description="Rotate the entire scene 90 degrees around the X axis so Y is up.", default=True)
+ FlipNormals = BoolProperty(name="Flip Normals", description="", default=False)
+ ApplyModifiers = BoolProperty(name="Apply Modifiers", description="Apply object modifiers before export.", default=False)
+ IncludeFrameRate = BoolProperty(name="Include Frame Rate", description="Include the AnimTicksPerSecond template which is used by some engines to control animation speed.", default=False)
+ ExportTextures = BoolProperty(name="Export Textures", description="Reference external image files to be used by the model.", default=True)
+ ExportArmatures = BoolProperty(name="Export Armatures", description="Export the bones of any armatures to deform meshes.", default=False)
+ ExportAnimation = EnumProperty(name="Animations", description="Select the type of animations to export. Only object and armature bone animations can be exported. Full Animation exports every frame.", items=AnimationModes, default="0")
+
+ #Export Mode
+ ExportMode = EnumProperty(name="Export", description="Select which objects to export. Only Mesh, Empty, and Armature objects will be exported.", items=ExportModes, default="1")
+
+ Verbose = BoolProperty(name="Verbose", description="Run the exporter in debug mode. Check the console for output.", default=False)
+
+ def execute(self, context):
+ #Append .x if needed
+ FilePath = self.filepath
+ if not FilePath.lower().endswith(".x"):
+ FilePath += ".x"
+
+ Config = DirectXExporterSettings(context,
+ FilePath,
+ CoordinateSystem=self.CoordinateSystem,
+ RotateX=self.RotateX,
+ FlipNormals=self.FlipNormals,
+ ApplyModifiers=self.ApplyModifiers,
+ IncludeFrameRate=self.IncludeFrameRate,
+ ExportTextures=self.ExportTextures,
+ ExportArmatures=self.ExportArmatures,
+ ExportAnimation=self.ExportAnimation,
+ ExportMode=self.ExportMode,
+ Verbose=self.Verbose)
+ ExportDirectX(Config)
+ return {"FINISHED"}
+
+ def invoke(self, context, event):
+ WindowManager = context.window_manager
+ WindowManager.fileselect_add(self)
+ return {"RUNNING_MODAL"}
+
+
+def menu_func(self, context):
+ default_path = os.path.splitext(bpy.data.filepath)[0] + ".x"
+ self.layout.operator(DirectXExporter.bl_idname, text="DirectX (.x)").filepath = default_path
+
+
+def register():
+ bpy.types.INFO_MT_file_export.append(menu_func)
+
+
+def unregister():
+ bpy.types.INFO_MT_file_export.remove(menu_func)
+
+
+if __name__ == "__main__":
+ register()