Newer
Older
# ***** 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 *****
NOTE: ========================================================
I've begun work on a Full Animation feature to more accurately export
FCurve data. It's going pretty well, but I'm having some trouble with
axis flipping. To "enable" the feature, uncomment line #988
The problem is in the WriteFullAnimationSet function at line #948
- Chris (2010-7-11)
bl_addon_info = {
'name': 'Export: DirectX Model Format (.x)',
'author': 'Chris Foster (Kira Vakaan)',
'version': '1.1',
'blender': (2, 5, 3),
'description': 'Export to the DirectX Model Format (.x)',
'warning': '', # used for warning icon and text in addons panel
'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?'\
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
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
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]
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()
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()
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=" ")
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...")
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 += len(Face.verts)
#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.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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
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):
LocalMatrix = Object.parent.matrix_world.copy().invert()
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
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]
BoneMatrix = (PoseBone.parent.matrix *
RotationMatrix(radians(-90), 4, "X")).invert()
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
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 Config.Verbose:
print(" Writing Mesh UV Coordinates...", end=" ")
WriteMeshUVCoordinates(Config, Mesh)
if Config.Verbose:
print("Done")
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))
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)))
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))
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)))
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
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))
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])
Config.File.write("{}1;\n{}1;\n{}0;;\n".format(" " * Config.Whitespace, " " * Config.Whitespace, " " * Config.Whitespace))
Config.Whitespace -= 1
Config.File.write("{}}} //End of {} Material List\n".format(" " * Config.Whitespace, LegalName(Mesh.name)))
def WriteMaterial(Config, Material=None):
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]))
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))
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:
Index = 0
VertexCount = GetMeshVertexCount(Mesh)
Config.File.write("{}{};\n".format(" " * Config.Whitespace, VertexCount))
for Vertex in Face.uv:
Vertices.append(tuple(Vertex))
if Config.CoordinateSystem == 1:
Vertices = Vertices[::-1]
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"]
ArmatureObject = ArmatureList[0].object
Armature = ArmatureObject.data
PoseBones = ArmatureObject.pose.bones
MaxInfluences = 0
UsedBones = set()
#Maps bones to a list of vertices they affect
#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))
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
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]:
VertexWeights.append(Mesh.verts[Vertex].groups[GroupIndexes[Bone.name]].weight / WeightTotal)
else:
VertexWeights.append(0.0)
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
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 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
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)))
if Config.Verbose:
print(" Writing Position...", end=" ")
AllKeyframes = set()
for Index, FCurve in enumerate(PositionFCurves):
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]))
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")
if Config.Verbose:
print(" Writing Rotation...", end=" ")
AllKeyframes = set()
for Index, FCurve in enumerate(RotationFCurves):
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]))
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")
if Config.Verbose:
print(" Writing Scale...", end=" ")
AllKeyframes = set()
for Index, FCurve in enumerate(ScaleFCurves):
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]))
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))
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
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")
Config.File.write("{}Animation {{\n".format(" " * Config.Whitespace))
Config.Whitespace += 1
Config.File.write("{}{{{}}}\n".format(" " * Config.Whitespace, LegalName(Object.name) + "_" + LegalName(Bone.name)))
if Config.Verbose:
print(" Writing Position...", end=" ")
AllKeyframes = set()
for Index, FCurve in enumerate(PositionFCurves):
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)
PoseMatrix = (Bone.parent.matrix * RotationMatrix(radians(-90), 4, "X")).invert()
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")
if Config.Verbose:
print(" Writing Rotation...", end=" ")
AllKeyframes = set()
for Index, FCurve in enumerate(RotationFCurves):
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)
PoseMatrix = (Bone.parent.matrix * RotationMatrix(radians(-90), 4, "X")).invert()
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")
if Config.Verbose:
print(" Writing Scale...", end=" ")
AllKeyframes = set()
for Index, FCurve in enumerate(ScaleFCurves):
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)
PoseMatrix = (Bone.parent.matrix * RotationMatrix(radians(-90), 4, "X")).invert()
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))
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
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:
Config.File.write("{}Animation {{\n".format(" " * Config.Whitespace))
Config.Whitespace += 1
Config.File.write("{}{{{}}}\n".format(" " * Config.Whitespace, LegalName(Object.name)))
#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.set_frame(Frame)
Position = Config.SystemMatrix * Object.location
Config.File.write("{}{}{:9f},{:9f},{:9f};;\n".format(" " * Config.Whitespace, (str(Frame) + ";3;").ljust(8), Position[0], Position[1], Position[2]))
Config.Whitespace -= 1
Config.File.write("{}}}\n".format(" " * Config.Whitespace))
#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.set_frame(Frame)
#Works pretty well, but causes a slightly noticeable axis flip at 180*
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,(str(Frame) + ";4;").ljust(8), -Rotation[0], Rotation[1], Rotation[2], Rotation[3]))
Config.Whitespace -= 1
Config.File.write("{}}}\n".format(" " * Config.Whitespace))
#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.set_frame(Frame)
Scale = Config.SystemMatrix * Object.scale
Config.File.write("{}{}{:9f},{:9f},{:9f};;\n".format(" " * Config.Whitespace,(str(Frame) + ";3;").ljust(8), Scale[0], Scale[1], Scale[2]))
Config.Whitespace -= 1
Config.File.write("{}}}\n".format(" " * Config.Whitespace))
Config.Whitespace -= 1
Config.File.write("{}}}\n".format(" " * Config.Whitespace))
if Config.ExportArmatures and Object.type == "ARMATURE":
pass
Config.Whitespace -= 1
Config.File.write("{}}} //End of AnimationSet\n".format(" " * Config.Whitespace))
if Config.Verbose:
print("Closing File...", end=" ")
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()
CoordinateSystem = EnumProperty(name="System", description="Select a coordinate system to export to", items=CoordinateSystems, default="1")
RotateX = BoolProperty(name="Rotate X 90 Degrees", description="Rotate the entire scene 90 degrees around the X axis so Y is up", default=True)