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 *****
Thomas Dinges
committed
"api": 31847,
"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/File_I-O/DirectX_Exporter",
"tracker_url": "https://projects.blender.org/tracker/index.php?"\
"func=detail&aid=22795&group_id=153&atid=469",
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")
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 *= Matrix.Rotation(radians(-90), 4, "X")
if Config.CoordinateSystem == 1:
Config.SystemMatrix *= Matrix.Scale(-1, 4, Vector((0, 1, 0)))
Config.InverseSystemMatrix = Config.SystemMatrix.copy().invert()
#Used for animating rotations
Config.SystemQuaternion = Quaternion((1,0,0,0))
if Config.RotateX:
Config.SystemQuaternion = Matrix.Rotation(radians(-90), 3, "X").to_quat()
Config.InverseSystemQuaternion = Config.SystemQuaternion.copy().inverse()
Config.FlipZ = -1 if Config.CoordinateSystem == 1 else 1
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):
#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 WriteObjects(Config, ObjectList):
Config.ObjectList += 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 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...", end=" ")
Chris Foster
committed
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")
Chris Foster
committed
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):
Chris Foster
committed
LocalMatrix = Config.SystemMatrix * Object.matrix_local * 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 *
Matrix.Rotation(radians(-90), 4, "X")).invert()
BoneMatrix *= PoseBone.matrix * Matrix.Rotation(radians(-90), 4, "X")
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
341
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))
if Config.CoordinateSystem == 1:
Vertices = Vertices[::-1]
for Vertex in [Mesh.vertices[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.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))
if Config.CoordinateSystem == 1:
Vertices = Vertices[::-1]
for Vertex in [Mesh.vertices[Vertex] for Vertex in Vertices]:
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.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
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
Chris Foster
committed
ArmatureBones = ArmatureObject.data.bones
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]]
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.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}
for Weight in [Group.weight for Group in Mesh.vertices[Vertex].groups if Object.vertex_groups[Group.group].name in PoseBones]:
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")
Chris Foster
committed
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 * Matrix.Rotation(radians(-90), 4, "X")).invert()
BoneMatrix *= ArmatureObject.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:
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.frame_set(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:
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.frame_set(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:
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.frame_set(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:
PoseMatrix = (Bone.parent.matrix * Matrix.Rotation(radians(-90), 4, "X")).invert()
PoseMatrix *= Bone.matrix * Matrix.Rotation(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:
PoseMatrix = (Bone.parent.matrix * Matrix.Rotation(radians(-90), 4, "X")).invert()
PoseMatrix *= Bone.matrix * Matrix.Rotation(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:
PoseMatrix = (Bone.parent.matrix * Matrix.Rotation(radians(-90), 4, "X")).invert()
PoseMatrix *= Bone.matrix * Matrix.Rotation(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") #Done with Armature Bone
print(" Done") #Done with Armature Bone data
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...", end=" ")
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 = 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))
if Config.Verbose:
print("Done")
#Rotation
if Config.Verbose:
print(" Writing Rotation...", end=" ")
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 = Config.SystemQuaternion.cross(Object.rotation_euler.to_quat().cross(Config.InverseSystemQuaternion))
Config.File.write("{}{}{:9f},{:9f},{:9f},{:9f};;\n".format(" " * Config.Whitespace, (str(Frame) + ";4;").ljust(8), Rotation[0], Rotation[1], Rotation[2], Config.FlipZ * 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=" ")
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 = 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))
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
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...", end=" ")
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 * Matrix.Rotation(radians(-90), 4, "X")).invert()
else:
PoseMatrix = Matrix()
PoseMatrix *= Bone.matrix * Matrix.Rotation(radians(-90), 4, "X")