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:
authorBrendon Murphy <meta.androcto1@gmail.com>2010-07-09 15:11:35 +0400
committerBrendon Murphy <meta.androcto1@gmail.com>2010-07-09 15:11:35 +0400
commit8558a92ff85c4d0bafa5df57178a92f53703c5f7 (patch)
treec882bc9e4dc4389a03c079d65c8c2749f4859484
parente63bd64d90b00a9ac0c223f3683c238166a2ca66 (diff)
Added port of direct x exporter to trunk.
Thanks to Chris Foster for this well written script.
-rw-r--r--export_directx_x.py870
1 files changed, 870 insertions, 0 deletions
diff --git a/export_directx_x.py b/export_directx_x.py
new file mode 100644
index 00000000..627226cb
--- /dev/null
+++ b/export_directx_x.py
@@ -0,0 +1,870 @@
+ # ***** 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 *****
+
+#One line description for early versions of Blender 2.52.
+"Export: DirectX Model Format (.x)"
+
+bl_addon_info = {
+ 'name': 'Export: DirectX Model Format (.x)',
+ 'author': 'Chris Foster (Kira Vakaan)',
+ 'version': '1.1',
+ 'blender': (2, 5, 3),
+ 'location': 'location":"File > Export',
+ 'description': 'Export to the DirectX Model Format',
+ 'wiki_url': 'http://wiki.blender.org/index.php/Extensions:2.5/Py/' \
+ 'Scripts/File_I-O/DirectX_Exporter',
+ 'tracker_url': 'https://projects.blender.org/tracker/index.php?'\
+ 'func=detail&aid=22795&group_id=153&atid=467',
+ 'category': 'Import/Export'}
+"""
+Name: 'DirectX Exporter'
+Blender: 252
+Group: 'Export'
+Tooltip: 'Exports to the DirectX model file format (.x)'
+"""
+
+__author__="Chris Foster (Kira Vakaan)"
+__url__="www.tobedecided.com"
+__version__="1.1"
+__bpydoc__="""\
+"""
+
+import bpy
+from math import radians
+from mathutils import *
+import os
+
+
+#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...",end=" ")
+ Config.File=open(Config.FilePath,"w")
+ if Config.Verbose: print("Done")
+
+ if Config.Verbose: print("Generating Object list for export...",end=" ")
+ if Config.ExportMode==1:
+ Config.ExportList=[Object for Object in Config.context.scene.objects if Object.type in ("ARMATURE","EMPTY","MESH") and Object.parent==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...",end=" ")
+ Config.SystemMatrix=Matrix()
+ if Config.RotateX:
+ Config.SystemMatrix*=RotationMatrix(radians(-90),4,"X")
+ if Config.CoordinateSystem==1:
+ Config.SystemMatrix*=ScaleMatrix(-1,4,Vector((0,1,0)))
+ Config.InverseSystemMatrix=Config.SystemMatrix.copy().invert()
+
+ 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...",end=" ")
+ WriteHeader(Config)
+ if Config.Verbose: print("Done")
+
+ Config.Whitespace=0
+ Config.ObjectList=[]
+ if Config.Verbose: print("Writing Objects...")
+ WriteObjects(Config,Config.ExportList)
+ if Config.Verbose: print("Done")
+
+ if Config.ExportAnimation:
+ if Config.IncludeFrameRate:
+ if Config.Verbose: print("Writing Frame Rate...",end=" ")
+ 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...")
+ WriteAnimationSet(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")]
+
+def GetMeshVertexCount(Mesh):
+ VertexCount=0
+ for Face in Mesh.faces:
+ VertexCount+=len(Face.verts)
+ return VertexCount
+
+def GetMaterialTexture(Material):
+ if Material:
+ ImageTextures=[Material.texture_slots[TextureSlot].texture for TextureSlot in Material.texture_slots.keys() if Material.texture_slots[TextureSlot].texture.type=="IMAGE"]
+ ImageFiles=[os.path.basename(Texture.image.filename) 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 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...",end=" ")
+ 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==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...",end=" ")
+ Mesh=Object.create_mesh(bpy.context.scene,(Config.ApplyModifiers|Config.ExportArmatures),"PREVIEW")
+ if Config.Verbose: print("Done")
+ if Config.Verbose: print(" Writing Mesh...")
+ WriteMesh(Config,Object,Mesh)
+ if Config.Verbose: print(" Done")
+ 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):
+ if Object.parent:
+ LocalMatrix=Object.parent.matrix_world.copy().invert()
+ else:
+ LocalMatrix=Matrix()
+ LocalMatrix*=Object.matrix_world
+ LocalMatrix=Config.SystemMatrix*LocalMatrix*Config.InverseSystemMatrix
+
+ 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),end=" ")
+ 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*RotationMatrix(radians(-90),4,"X")).invert()
+ else:
+ BoneMatrix=Matrix()
+
+ BoneMatrix*=PoseBone.matrix*RotationMatrix(radians(-90),4,"X")
+ BoneMatrix=Config.SystemMatrix*BoneMatrix*Config.InverseSystemMatrix
+
+ 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...",end=" ")
+ WriteMeshVertices(Config,Mesh)
+ if Config.Verbose: print("Done\n Writing Mesh Normals...",end=" ")
+ WriteMeshNormals(Config,Mesh)
+ if Config.Verbose: print("Done\n Writing Mesh Materials...",end=" ")
+ WriteMeshMaterials(Config,Mesh)
+ if Config.Verbose: print("Done")
+ if Mesh.uv_textures:
+ if Config.Verbose: print(" Writing Mesh UV Coordinates...",end=" ")
+ WriteMeshUVCoordinates(Config,Mesh)
+ if Config.Verbose: print("Done")
+ if Config.ExportArmatures:
+ if Config.Verbose: print(" Writing Mesh Skin Weights...",end=" ")
+ 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.verts)
+
+ if Config.CoordinateSystem==1: Vertices=Vertices[::-1]
+ for Vertex in [Mesh.verts[Vertex] for Vertex in Vertices]:
+ Position=Config.SystemMatrix*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.verts)))
+ for Vertex in Face.verts:
+ 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.verts)
+
+ if Config.CoordinateSystem==1: Vertices=Vertices[::-1]
+ for Vertex in [Mesh.verts[Vertex] for Vertex in Vertices]:
+ if Face.smooth: Normal=Config.SystemMatrix*Vertex.normal
+ else: Normal=Config.SystemMatrix*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.verts)))
+ for Vertex in Face.verts:
+ 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
+ Armature=ArmatureObject.data
+
+ PoseBones=ArmatureObject.pose.bones
+
+ MaxInfluences=0
+ UsedBones=set()
+ VertexGroups={}
+
+ for Vertex in Mesh.verts:
+ 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.verts:
+ 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.verts)
+ 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.verts[Vertex].groups) if Object.vertex_groups[Group.group].name in PoseBones}
+
+ WeightTotal=0.0
+ for Weight in [Group.weight for Group in Mesh.verts[Vertex].groups if Object.vertex_groups[Group.group].name in PoseBones]:
+ WeightTotal+=Weight
+
+ if WeightTotal:
+ VertexWeights.append(Mesh.verts[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")
+
+ PoseBone=PoseBones[Bone.name]
+
+ BoneMatrix=(PoseBone.matrix*RotationMatrix(radians(-90),4,"X")).invert()
+ BoneMatrix*=ArmatureObject.matrix_world.copy().invert()
+
+ if Object.parent and Object.parent!=ArmatureObject:
+ BoneMatrix*=Object.parent.matrix_world.copy().invert()
+
+ BoneMatrix*=Object.matrix_world
+
+ BoneMatrix=Config.SystemMatrix*BoneMatrix*Config.InverseSystemMatrix
+
+ 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 WriteAnimationSet(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...",end=" ")
+ AllKeyframes=set()
+ for Index,FCurve in enumerate(PositionFCurves):
+ if FCurve:
+ Keyframes=[]
+ for Keyframe in FCurve.keyframe_points:
+ 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.set_frame(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])
+ Position=Config.SystemMatrix*Position
+ Config.File.write("{}{}{:9f},{:9f},{:9f};;\n".format(" "*Config.Whitespace,(str(Keyframe-bpy.context.scene.frame_start)+";3;").ljust(8),Position[0],Position[1],Position[2]))
+ else:
+ Config.File.write("{}2;\n{}1;\n".format(" "*Config.Whitespace," "*Config.Whitespace))
+ bpy.context.scene.set_frame(bpy.context.scene.frame_start)
+ Position=Config.SystemMatrix*Object.location
+ 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...",end=" ")
+ AllKeyframes=set()
+ for Index,FCurve in enumerate(RotationFCurves):
+ if FCurve:
+ Keyframes=[]
+ for Keyframe in FCurve.keyframe_points:
+ 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.set_frame(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=(Config.SystemMatrix*(Rotation.to_matrix().to_4x4())*Config.InverseSystemMatrix).to_quat()
+ Config.File.write("{}{}{:9f},{:9f},{:9f},{:9f};;\n".format(" "*Config.Whitespace,(str(Keyframe-bpy.context.scene.frame_start)+";4;").ljust(8),-Rotation[0],Rotation[1],Rotation[2],Rotation[3]))
+ else:
+ Config.File.write("{}0;\n{}1;\n".format(" "*Config.Whitespace," "*Config.Whitespace))
+ bpy.context.scene.set_frame(bpy.context.scene.frame_start)
+ Rotation=(Config.SystemMatrix*(Object.rotation_euler.to_matrix().to_4x4())*Config.InverseSystemMatrix).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...",end=" ")
+ AllKeyframes=set()
+ for Index,FCurve in enumerate(ScaleFCurves):
+ if FCurve:
+ Keyframes=[]
+ for Keyframe in FCurve.keyframe_points:
+ 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.set_frame(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])
+ Scale=Config.SystemMatrix*Scale
+ Config.File.write("{}{}{:9f},{:9f},{:9f};;\n".format(" "*Config.Whitespace,(str(Keyframe-bpy.context.scene.frame_start)+";3;").ljust(8),Scale[0],Scale[1],Scale[2]))
+ else:
+ Config.File.write("{}1;\n{}1;\n".format(" "*Config.Whitespace," "*Config.Whitespace))
+ bpy.context.scene.set_frame(bpy.context.scene.frame_start)
+ Scale=Config.SystemMatrix*Object.scale
+ 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...",end=" ")
+ AllKeyframes=set()
+ for Index,FCurve in enumerate(PositionFCurves):
+ if FCurve:
+ Keyframes=[]
+ for Keyframe in FCurve.keyframe_points:
+ 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.set_frame(Keyframe)
+
+ if Bone.parent:
+ PoseMatrix=(Bone.parent.matrix*RotationMatrix(radians(-90),4,"X")).invert()
+ else:
+ PoseMatrix=Matrix()
+ PoseMatrix*=Bone.matrix*RotationMatrix(radians(-90),4,"X")
+
+ PoseMatrix=Config.SystemMatrix*PoseMatrix*Config.InverseSystemMatrix
+
+ Position=PoseMatrix.translation_part()
+ Config.File.write("{}{}{:9f},{:9f},{:9f};;\n".format(" "*Config.Whitespace,(str(Keyframe-bpy.context.scene.frame_start)+";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...",end=" ")
+ AllKeyframes=set()
+ for Index,FCurve in enumerate(RotationFCurves):
+ if FCurve:
+ Keyframes=[]
+ for Keyframe in FCurve.keyframe_points:
+ 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.set_frame(Keyframe)
+
+ if Bone.parent:
+ PoseMatrix=(Bone.parent.matrix*RotationMatrix(radians(-90),4,"X")).invert()
+ else:
+ PoseMatrix=Matrix()
+ PoseMatrix*=Bone.matrix*RotationMatrix(radians(-90),4,"X")
+
+ PoseMatrix=Config.SystemMatrix*PoseMatrix*Config.InverseSystemMatrix
+
+ Rotation=PoseMatrix.rotation_part().to_quat()
+ Config.File.write("{}{}{:9f},{:9f},{:9f},{:9f};;\n".format(" "*Config.Whitespace,(str(Keyframe-bpy.context.scene.frame_start)+";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...",end=" ")
+ AllKeyframes=set()
+ for Index,FCurve in enumerate(ScaleFCurves):
+ if FCurve:
+ Keyframes=[]
+ for Keyframe in FCurve.keyframe_points:
+ 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.set_frame(Keyframe)
+
+ if Bone.parent:
+ PoseMatrix=(Bone.parent.matrix*RotationMatrix(radians(-90),4,"X")).invert()
+ else:
+ PoseMatrix=Matrix()
+ PoseMatrix*=Bone.matrix*RotationMatrix(radians(-90),4,"X")
+
+ PoseMatrix=Config.SystemMatrix*PoseMatrix*Config.InverseSystemMatrix
+
+ Scale=PoseMatrix.scale_part()
+ Config.File.write("{}{}{:9f},{:9f},{:9f};;\n".format(" "*Config.Whitespace,(str(Keyframe-bpy.context.scene.frame_start)+";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))
+ if Config.Verbose: print(" Done")
+ if Config.Verbose: print(" Done")
+ if Config.Verbose: print(" Done")
+
+ Config.Whitespace-=1
+ Config.File.write("{}}} //End of AnimationSet\n".format(" "*Config.Whitespace))
+
+def CloseFile(Config):
+ if Config.Verbose: print("Closing File...",end=" ")
+ 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()
+ filename=StringProperty()
+ directory=StringProperty()
+
+ #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 all 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. Warning: This option also applies all modifiers.",default=False)
+ ExportAnimation=EnumProperty(name="Animations",description="Select the type of animations to export. Only object and armature bone animations can be exported.",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.properties.filepath
+ if not FilePath.lower().endswith(".x"):
+ FilePath+=".x"
+
+ Config=DirectXExporterSettings(context,
+ FilePath,
+ CoordinateSystem=self.properties.CoordinateSystem,
+ RotateX=self.properties.RotateX,
+ FlipNormals=self.properties.FlipNormals,
+ ApplyModifiers=self.properties.ApplyModifiers,
+ IncludeFrameRate=self.properties.IncludeFrameRate,
+ ExportTextures=self.properties.ExportTextures,
+ ExportArmatures=self.properties.ExportArmatures,
+ ExportAnimation=self.properties.ExportAnimation,
+ ExportMode=self.properties.ExportMode,
+ Verbose=self.properties.Verbose)
+ ExportDirectX(Config)
+ return {"FINISHED"}
+
+ def invoke(self,context,event):
+ WindowManager=context.manager
+ WindowManager.add_fileselect(self)
+ return {"RUNNING_MODAL"}
+
+def menu_func(self,context):
+ DefaultPath=bpy.data.filepath
+ if DefaultPath.endswith(".blend"):
+ DefaultPath=DefaultPath[:-6]+".x"
+ self.layout.operator(DirectXExporter.bl_idname,text="DirectX (.x)").filepath=DefaultPath
+
+def register():
+ bpy.types.register(DirectXExporter)
+ bpy.types.INFO_MT_file_export.append(menu_func)
+
+def unregister():
+ bpy.types.unregister(DirectXExporter)
+ bpy.types.INFO_MT_file_export.remove(menu_func)
+
+if __name__=="__main__":
+ register() \ No newline at end of file