Skip to content
Snippets Groups Projects
export_fbx_bin.py 136 KiB
Newer Older
# ##### BEGIN 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 2
#  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, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

# <pep8 compliant>

# Script copyright (C) Campbell Barton, Bastien Montagne


import array
import datetime
import math
import os
import time

import collections
from collections import namedtuple, OrderedDict
import itertools
from itertools import zip_longest, chain

import bpy
import bpy_extras
from bpy.types import Object, Bone, PoseBone
from mathutils import Vector, Matrix

from . import encode_bin, data_types


# "Constants"
FBX_VERSION = 7400
FBX_HEADER_VERSION = 1003
FBX_SCENEINFO_VERSION = 100
FBX_TEMPLATES_VERSION = 100

FBX_MODELS_VERSION = 232

FBX_GEOMETRY_VERSION = 124
# Revert back normals to 101 (simple 3D values) for now, 102 (4D + weights) seems not well supported by most apps
# currently, apart from some AD products.
FBX_GEOMETRY_NORMAL_VERSION = 101
FBX_GEOMETRY_BINORMAL_VERSION = 101
FBX_GEOMETRY_TANGENT_VERSION = 101
FBX_GEOMETRY_SMOOTHING_VERSION = 102
FBX_GEOMETRY_VCOLOR_VERSION = 101
FBX_GEOMETRY_UV_VERSION = 101
FBX_GEOMETRY_MATERIAL_VERSION = 101
FBX_GEOMETRY_LAYER_VERSION = 100
FBX_POSE_BIND_VERSION = 100
FBX_DEFORMER_SKIN_VERSION = 101
FBX_DEFORMER_CLUSTER_VERSION = 100
FBX_MATERIAL_VERSION = 102
FBX_TEXTURE_VERSION = 202
FBX_ANIM_KEY_VERSION = 4008

FBX_NAME_CLASS_SEP = b"\x00\x01"

FBX_KTIME = 46186158000  # This is the number of "ktimes" in one second (yep, precision over the nanosecond...)


MAT_CONVERT_LAMP = Matrix.Rotation(math.pi / 2.0, 4, 'X')  # Blender is -Z, FBX is -Y.
MAT_CONVERT_CAMERA = Matrix.Rotation(math.pi / 2.0, 4, 'Y')  # Blender is -Z, FBX is +X.
Bastien Montagne's avatar
Bastien Montagne committed
#MAT_CONVERT_BONE = Matrix.Rotation(math.pi / -2.0, 4, 'X')  # Blender is +Y, FBX is +Z.
MAT_CONVERT_BONE = Matrix()
BLENDER_OTHER_OBJECT_TYPES = {'CURVE', 'SURFACE', 'FONT', 'META'}
BLENDER_OBJECT_TYPES_MESHLIKE = {'MESH'} | BLENDER_OTHER_OBJECT_TYPES


# Lamps.
FBX_LIGHT_TYPES = {
    'POINT': 0,  # Point.
    'SUN': 1,    # Directional.
    'SPOT': 2,   # Spot.
    'HEMI': 1,   # Directional.
    'AREA': 3,   # Area.
}
FBX_LIGHT_DECAY_TYPES = {
    'CONSTANT': 0,                   # None.
    'INVERSE_LINEAR': 1,             # Linear.
    'INVERSE_SQUARE': 2,             # Quadratic.
    'CUSTOM_CURVE': 2,               # Quadratic.
    'LINEAR_QUADRATIC_WEIGHTED': 2,  # Quadratic.
}


##### Misc utilities #####

# Note: this could be in a utility (math.units e.g.)...

UNITS = {
    "meter": 1.0,  # Ref unit!
    "kilometer": 0.001,
    "millimeter": 1000.0,
    "foot": 1.0 / 0.3048,
    "inch": 1.0 / 0.0254,
    "turn": 1.0,  # Ref unit!
    "degree": 360.0,
    "radian": math.pi * 2.0,
    "second": 1.0,  # Ref unit!
    "ktime": FBX_KTIME,
}

def units_convert(val, u_from, u_to):
    """Convert value."""
    conv = UNITS[u_to] / UNITS[u_from]
    return val * conv


def units_convert_iter(it, u_from, u_to):
    """Convert value."""
    conv = UNITS[u_to] / UNITS[u_from]
    return (v * conv for v in it)


def matrix_to_array(mat):
    """Concatenate matrix's columns into a single, flat tuple"""
    # blender matrix is row major, fbx is col major so transpose on write
    return tuple(f for v in mat.transposed() for f in v)


def similar_values(v1, v2, e=1e-6):
    """Return True if v1 and v2 are nearly the same."""
    return abs(v1 - v2) * max(abs(v1), abs(v2)) <= e

RIGHT_HAND_AXES = {
    # Up, Front -> FBX values (tuples of (axis, sign), Up, Front, Coord).
    # Note: Since we always stay in right-handed system, third coord sign is always positive!
    ('X',  'Y'):  ((0, 1),  (1, -1),  (2, 1)),
    ('X',  '-Y'): ((0, 1),  (1, 1), (2, 1)),
    ('X',  'Z'):  ((0, 1),  (2, -1),  (1, 1)),
    ('X',  '-Z'): ((0, 1),  (2, 1), (1, 1)),
    ('-X', 'Y'):  ((0, -1), (1, -1),  (2, 1)),
    ('-X', '-Y'): ((0, -1), (1, 1), (2, 1)),
    ('-X', 'Z'):  ((0, -1), (2, -1),  (1, 1)),
    ('-X', '-Z'): ((0, -1), (2, 1), (1, 1)),
    ('Y',  'X'):  ((1, 1),  (0, -1),  (2, 1)),
    ('Y',  '-X'): ((1, 1),  (0, 1), (2, 1)),
    ('Y',  'Z'):  ((1, 1),  (2, -1),  (0, 1)),
    ('Y',  '-Z'): ((1, 1),  (2, 1), (0, 1)),
    ('-Y', 'X'):  ((1, -1), (0, -1),  (2, 1)),
    ('-Y', '-X'): ((1, -1), (0, 1), (2, 1)),
    ('-Y', 'Z'):  ((1, -1), (2, -1),  (0, 1)),
    ('-Y', '-Z'): ((1, -1), (2, 1), (0, 1)),
    ('Z',  'X'):  ((2, 1),  (0, -1),  (1, 1)),
    ('Z',  '-X'): ((2, 1),  (0, 1), (1, 1)),
    ('Z',  'Y'):  ((2, 1),  (1, -1),  (0, 1)),  # Blender system!
    ('Z',  '-Y'): ((2, 1),  (1, 1), (0, 1)),
    ('-Z', 'X'):  ((2, -1), (0, -1),  (1, 1)),
    ('-Z', '-X'): ((2, -1), (0, 1), (1, 1)),
    ('-Z', 'Y'):  ((2, -1), (1, -1),  (0, 1)),
    ('-Z', '-Y'): ((2, -1), (1, 1), (0, 1)),
FBX_FRAMERATES = (
    (-1.0, 14),  # Custom framerate.
    (120.0, 1),
    (100.0, 2),
    (60.0, 3),
    (50.0, 4),
    (48.0, 5),
    (30.0, 6),  # BW NTSC.
    (30.0 / 1.001, 9),  # Color NTSC.
    (25.0, 10),
    (24.0, 11),
    (24.0 / 1.001, 13),
    (96.0, 15),
    (72.0, 16),
    (60.0 / 1.001, 17),
)


##### UIDs code. #####

# ID class (mere int).
class UID(int):
    pass


# UIDs storage.
_keys_to_uids = {}
_uids_to_keys = {}


Loading
Loading full blame...