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_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.
#MAT_CONVERT_BONE = Matrix.Rotation(math.pi / -2.0, 4, 'X') # Blender is +Y, FBX is +Z.
MAT_CONVERT_BONE = Matrix()
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# 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)
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)),
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
}
##### UIDs code. #####
# ID class (mere int).
class UID(int):
pass
# UIDs storage.
_keys_to_uids = {}
_uids_to_keys = {}
def _key_to_uid(uids, key):
# TODO: Check this is robust enough for our needs!
# Note: We assume we have already checked the related key wasn't yet in _keys_to_uids!
# As int64 is signed in FBX, we keep uids below 2**63...
if isinstance(key, int) and 0 <= key < 2**63:
# We can use value directly as id!
uid = key
else:
uid = hash(key)
if uid < 0:
uid = -uid
if uid >= 2**63:
uid //= 2
Bastien Montagne
committed
# Try to make our uid shorter!
if uid > int(1e9):
t_uid = uid % int(1e9)
if t_uid not in uids:
uid = t_uid
# Make sure our uid *is* unique.
if uid in uids:
inc = 1 if uid < 2**62 else -1
while uid in uids:
uid += inc
if 0 > uid >= 2**63:
# Note that this is more that unlikely, but does not harm anyway...
raise ValueError("Unable to generate an UID for key {}".format(key))
Loading
Loading full blame...