Skip to content
Snippets Groups Projects
io_export_directx_x.py 50.7 KiB
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 *****

bl_addon_info = {
    'name': 'Export: DirectX Model Format (.x)',
    'author': 'Chris Foster (Kira Vakaan)',
    'blender': (2, 5, 3),
Luca Bonavita's avatar
Luca Bonavita committed
    'location': 'File > Export',
    '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?'\
Luca Bonavita's avatar
Luca Bonavita committed
        'func=detail&aid=22795&group_id=153&atid=469',
    'category': 'Import/Export'}
import bpy
from mathutils import *

#Container for the exporter settings
class DirectXExporterSettings:
    def __init__(self,
                 context,
                 FilePath,
                 CoordinateSystem=1,
                 RotateX=True,
                 FlipNormals=False,
                 ApplyModifiers=False,
                 IncludeFrameRate=False,
                 ExportTextures=True,
                 ExportArmatures=False,
                 ExportAnimation=0,
                 ExportMode=1,
                 Verbose=False):
        self.context = context
        self.FilePath = FilePath
        self.CoordinateSystem = int(CoordinateSystem)
        self.RotateX = RotateX
        self.FlipNormals = FlipNormals
        self.ApplyModifiers = ApplyModifiers
        self.IncludeFrameRate = IncludeFrameRate
        self.ExportTextures = ExportTextures
        self.ExportArmatures = ExportArmatures
        self.ExportAnimation = int(ExportAnimation)
        self.ExportMode = int(ExportMode)
        self.Verbose = Verbose


def LegalName(Name):
    NewName = Name.replace(".", "_")
    NewName = NewName.replace(" ", "_")
    if NewName[0].isdigit() or NewName in ["ARRAY",
                                           "DWORD",
                                           "UCHAR",
                                           "BINARY",
                                           "FLOAT",
                                           "ULONGLONG",
                                           "BINARY_RESOURCE",
                                           "SDWORD",
                                           "UNICODE",
                                           "CHAR",
                                           "STRING",
                                           "WORD",
                                           "CSTRING",
                                           "SWORD",
                                           "DOUBLE",
                                           "TEMPLATE"]:
        NewName = "_" + NewName
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()
    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...")
        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):
    for Face in Mesh.faces:
        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\
Loading
Loading full blame...