Fix FBX char type being interpreted as bool #104914

Merged
Thomas Barlow merged 2 commits from Mysteryem/blender-addons:fbx_fix_char_type into main 2023-09-26 03:44:45 +02:00
8 changed files with 38 additions and 11 deletions

View File

@ -5,7 +5,7 @@
bl_info = {
"name": "FBX format",
"author": "Campbell Barton, Bastien Montagne, Jens Restemeier, @Mysteryem",
"version": (5, 8, 1),
"version": (5, 8, 2),
"blender": (3, 6, 0),
"location": "File > Import-Export",
"description": "FBX IO meshes, UVs, vertex colors, materials, textures, cameras, lamps and actions",

View File

@ -3,7 +3,8 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
BOOL = b'C'[0]
BOOL = b'B'[0]
CHAR = b'C'[0]
INT8 = b'Z'[0]
INT16 = b'Y'[0]
INT32 = b'I'[0]

View File

@ -56,6 +56,14 @@ class FBXElem:
self.props_type.append(data_types.BOOL)
self.props.append(data)
def add_char(self, data):
assert(isinstance(data, bytes))
assert(len(data) == 1)
data = pack('<c', data)
self.props_type.append(data_types.CHAR)
self.props.append(data)
def add_int8(self, data):
assert(isinstance(data, int))
data = pack('<b', data)

View File

@ -66,7 +66,7 @@ from .fbx_utils import (
get_blender_nodetexture_key,
# FBX element data.
elem_empty,
elem_data_single_bool, elem_data_single_int16, elem_data_single_int32, elem_data_single_int64,
elem_data_single_char, elem_data_single_int16, elem_data_single_int32, elem_data_single_int64,
elem_data_single_float32, elem_data_single_float64,
elem_data_single_bytes, elem_data_single_string, elem_data_single_string_unicode,
elem_data_single_bool_array, elem_data_single_int32_array, elem_data_single_int64_array,
@ -1900,7 +1900,8 @@ def fbx_data_leaf_bone_elements(root, scene_data):
# object type, etc.
elem_data_single_int32(model, b"MultiLayer", 0)
elem_data_single_int32(model, b"MultiTake", 0)
elem_data_single_bool(model, b"Shading", True)
# Probably the FbxNode.EShadingMode enum. Full description in fbx_data_object_elements.
elem_data_single_char(model, b"Shading", b"\x01")
elem_data_single_string(model, b"Culling", b"CullingOff")
elem_props_template_finalize(tmpl, props)
@ -1964,7 +1965,12 @@ def fbx_data_object_elements(root, ob_obj, scene_data):
# object type, etc.
elem_data_single_int32(model, b"MultiLayer", 0)
elem_data_single_int32(model, b"MultiTake", 0)
elem_data_single_bool(model, b"Shading", True)
# This is probably the FbxNode.EShadingMode enum. Not directly used by the FBX SDK, but the SDK guarantees that the
# value will be passed through from an imported file to an exported one. Common values are 'Y' and 'T'. 'U' and 'W'
# have also been seen in older FBX files. It's not clear which enum member each of these values corresponds to or if
# these values are actually application specific. Blender had been exporting this as a `True` bool for a long time
# seemingly without issue. The '\x01' char is the same value as `True` in raw bytes.
elem_data_single_char(model, b"Shading", b"\x01")
elem_data_single_string(model, b"Culling", b"CullingOff")
if obj_type == b"Camera":

View File

@ -30,7 +30,8 @@ The types are as follows:
* 'Z': - INT8
* 'Y': - INT16
* 'C': - BOOL
* 'B': - BOOL
* 'C': - CHAR
* 'I': - INT32
* 'F': - FLOAT32
* 'D': - FLOAT64
@ -109,7 +110,8 @@ def unpack_array(read, array_type, array_stride, array_byteswap):
read_data_dict = {
b'Z'[0]: lambda read: unpack(b'<b', read(1))[0], # 8 bit int
b'Y'[0]: lambda read: unpack(b'<h', read(2))[0], # 16 bit int
b'C'[0]: lambda read: unpack(b'?', read(1))[0], # 1 bit bool (yes/no)
b'B'[0]: lambda read: unpack(b'?', read(1))[0], # 1 bit bool (yes/no)
b'C'[0]: lambda read: unpack(b'<c', read(1))[0], # char
b'I'[0]: lambda read: unpack(b'<i', read(4))[0], # 32 bit int
b'F'[0]: lambda read: unpack(b'<f', read(4))[0], # 32 bit float
b'D'[0]: lambda read: unpack(b'<d', read(8))[0], # 64 bit float
@ -225,7 +227,8 @@ data_types.__dict__.update(
dict(
INT8 = b'Z'[0],
INT16 = b'Y'[0],
BOOL = b'C'[0],
BOOL = b'B'[0],
CHAR = b'C'[0],
INT32 = b'I'[0],
FLOAT32 = b'F'[0],
FLOAT64 = b'D'[0],

View File

@ -969,6 +969,10 @@ def elem_data_single_bool(elem, name, value):
return _elem_data_single(elem, name, value, "add_bool")
def elem_data_single_char(elem, name, value):
return _elem_data_single(elem, name, value, "add_char")
def elem_data_single_int8(elem, name, value):
return _elem_data_single(elem, name, value, "add_int8")

View File

@ -29,7 +29,8 @@ The types are as follows:
* 'Z': - INT8
* 'Y': - INT16
* 'C': - BOOL
* 'B': - BOOL
* 'C': - CHAR
* 'I': - INT32
* 'F': - FLOAT32
* 'D': - FLOAT64
@ -64,8 +65,11 @@ def parse_json_rec(fbx_root, json_node):
e = elem_empty(fbx_root, name.encode())
for d, dt in zip(data, data_types):
if dt == "C":
if dt == "B":
e.add_bool(d)
elif dt == "C":
d = eval('b"""' + d + '"""')
e.add_char(d)
elif dt == "Z":
e.add_int8(d)
elif dt == "Y":

View File

@ -80,7 +80,8 @@ def unpack_array(read, array_type, array_stride, array_byteswap):
read_data_dict = {
b'Z'[0]: lambda read: unpack(b'<b', read(1))[0], # byte
b'Y'[0]: lambda read: unpack(b'<h', read(2))[0], # 16 bit int
b'C'[0]: lambda read: unpack(b'?', read(1))[0], # 1 bit bool (yes/no)
b'B'[0]: lambda read: unpack(b'?', read(1))[0], # 1 bit bool (yes/no)
b'C'[0]: lambda read: unpack(b'<c', read(1))[0], # char
b'I'[0]: lambda read: unpack(b'<i', read(4))[0], # 32 bit int
b'F'[0]: lambda read: unpack(b'<f', read(4))[0], # 32 bit float
b'D'[0]: lambda read: unpack(b'<d', read(8))[0], # 64 bit float