diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py index a58e7a5c1..66bb8e3a6 100644 --- a/io_scene_fbx/export_fbx_bin.py +++ b/io_scene_fbx/export_fbx_bin.py @@ -1719,6 +1719,14 @@ def fbx_data_video_elements(root, vid, scene_data): #~ else: #~ elem_data_single_bytes(fbx_vid, b"Content", b"") + # Blender currently has no UI for editing custom properties on Images, but the importer will import Image custom + # properties from either a Video Node or a Texture Node, preferring a Video node if one exists. We'll propagate + # these custom properties only to Video Nodes because that is most likely where they were imported from, and Texture + # Nodes are more like Blender's Shader Nodes than Images, which is what we're exporting here. + if scene_data.settings.use_custom_props: + fbx_data_element_custom_properties(props, vid) + + def fbx_data_armature_elements(root, arm_obj, scene_data): """ diff --git a/io_scene_fbx/import_fbx.py b/io_scene_fbx/import_fbx.py index 50d23db78..961594417 100644 --- a/io_scene_fbx/import_fbx.py +++ b/io_scene_fbx/import_fbx.py @@ -1777,10 +1777,12 @@ def blen_read_texture_image(fbx_tmpl, fbx_obj, basedir, settings): return image -def blen_read_camera(fbx_tmpl, fbx_obj, global_scale): +def blen_read_camera(fbx_tmpl, fbx_obj, settings): # meters to inches M2I = 0.0393700787 + global_scale = settings.global_scale + elem_name_utf8 = elem_name_ensure_class(fbx_obj, b'NodeAttribute') fbx_props = (elem_find_first(fbx_obj, b'Properties70'), @@ -1808,10 +1810,13 @@ def blen_read_camera(fbx_tmpl, fbx_obj, global_scale): camera.clip_start = elem_props_get_number(fbx_props, b'NearPlane', 0.01) * global_scale camera.clip_end = elem_props_get_number(fbx_props, b'FarPlane', 100.0) * global_scale + if settings.use_custom_props: + blen_read_custom_properties(fbx_obj, camera, settings) + return camera -def blen_read_light(fbx_tmpl, fbx_obj, global_scale): +def blen_read_light(fbx_tmpl, fbx_obj, settings): import math elem_name_utf8 = elem_name_ensure_class(fbx_obj, b'NodeAttribute') @@ -1841,13 +1846,16 @@ def blen_read_light(fbx_tmpl, fbx_obj, global_scale): # TODO, cycles nodes??? lamp.color = elem_props_get_color_rgb(fbx_props, b'Color', (1.0, 1.0, 1.0)) lamp.energy = elem_props_get_number(fbx_props, b'Intensity', 100.0) / 100.0 - lamp.distance = elem_props_get_number(fbx_props, b'DecayStart', 25.0) * global_scale + lamp.distance = elem_props_get_number(fbx_props, b'DecayStart', 25.0) * settings.global_scale lamp.use_shadow = elem_props_get_bool(fbx_props, b'CastShadow', True) if hasattr(lamp, "cycles"): lamp.cycles.cast_shadow = lamp.use_shadow # Keeping this for now, but this is not used nor exposed anymore afaik... lamp.shadow_color = elem_props_get_color_rgb(fbx_props, b'ShadowColor', (0.0, 0.0, 0.0)) + if settings.use_custom_props: + blen_read_custom_properties(fbx_obj, lamp, settings) + return lamp @@ -1861,7 +1869,7 @@ class FbxImportHelperNode: __slots__ = ( '_parent', 'anim_compensation_matrix', 'is_global_animation', 'armature_setup', 'armature', 'bind_matrix', 'bl_bone', 'bl_data', 'bl_obj', 'bone_child_matrix', 'children', 'clusters', - 'fbx_elem', 'fbx_name', 'fbx_transform_data', 'fbx_type', + 'fbx_elem', 'fbx_data_elem', 'fbx_name', 'fbx_transform_data', 'fbx_type', 'is_armature', 'has_bone_children', 'is_bone', 'is_root', 'is_leaf', 'matrix', 'matrix_as_parent', 'matrix_geom', 'meshes', 'post_matrix', 'pre_matrix') @@ -1869,6 +1877,7 @@ class FbxImportHelperNode: self.fbx_name = elem_name_ensure_class(fbx_elem, b'Model') if fbx_elem else 'Unknown' self.fbx_type = fbx_elem.props[2] if fbx_elem else None self.fbx_elem = fbx_elem + self.fbx_data_elem = None # FBX elem of a connected NodeAttribute/Geometry for helpers whose bl_data does not exist or is yet to be created. self.bl_obj = None self.bl_data = bl_data self.bl_bone = None # Name of bone if this is a bone (this may be different to fbx_name if there was a name conflict in Blender!) @@ -2199,7 +2208,7 @@ class FbxImportHelperNode: for child in self.children: child.collect_armature_meshes() - def build_skeleton(self, arm, parent_matrix, parent_bone_size=1, force_connect_children=False): + def build_skeleton(self, arm, parent_matrix, settings, parent_bone_size=1): def child_connect(par_bone, child_bone, child_head, connect_ctx): # child_bone or child_head may be None. force_connect_children, connected = connect_ctx @@ -2246,6 +2255,9 @@ class FbxImportHelperNode: self.bl_obj = arm.bl_obj self.bl_data = arm.bl_data self.bl_bone = bone.name # Could be different from the FBX name! + # Read EditBone custom props the NodeAttribute + if settings.use_custom_props and self.fbx_data_elem: + blen_read_custom_properties(self.fbx_data_elem, bone, settings) # get average distance to children bone_size = 0.0 @@ -2274,6 +2286,7 @@ class FbxImportHelperNode: # while Blender attaches to the tail. self.bone_child_matrix = Matrix.Translation(-bone_tail) + force_connect_children = settings.force_connect_children connect_ctx = [force_connect_children, ...] for child in self.children: if child.is_leaf and force_connect_children: @@ -2282,8 +2295,7 @@ class FbxImportHelperNode: child_head = (bone_matrix @ child.get_bind_matrix().normalized()).translation child_connect(bone, None, child_head, connect_ctx) elif child.is_bone and not child.ignore: - child_bone = child.build_skeleton(arm, bone_matrix, bone_size, - force_connect_children=force_connect_children) + child_bone = child.build_skeleton(arm, bone_matrix, settings, bone_size) # Connection to parent. child_connect(bone, child_bone, None, connect_ctx) @@ -2378,15 +2390,18 @@ class FbxImportHelperNode: return obj - def set_pose_matrix(self, arm): + def set_pose_matrix_and_custom_props(self, arm, settings): pose_bone = arm.bl_obj.pose.bones[self.bl_bone] pose_bone.matrix_basis = self.get_bind_matrix().inverted_safe() @ self.get_matrix() + if settings.use_custom_props: + blen_read_custom_properties(self.fbx_elem, pose_bone, settings) + for child in self.children: if child.ignore: continue if child.is_bone: - child.set_pose_matrix(arm) + child.set_pose_matrix_and_custom_props(arm, settings) def merge_weights(self, combined_weights, fbx_cluster): indices = elem_prop_first(elem_find_first(fbx_cluster, b'Indexes', default=None), default=()) @@ -2482,18 +2497,18 @@ class FbxImportHelperNode: if child.ignore: continue if child.is_bone: - child.build_skeleton(self, Matrix(), force_connect_children=settings.force_connect_children) + child.build_skeleton(self, Matrix(), settings) bpy.ops.object.mode_set(mode='OBJECT') arm.hide_viewport = is_hidden - # Set pose matrix + # Set pose matrix and PoseBone custom properties for child in self.children: if child.ignore: continue if child.is_bone: - child.set_pose_matrix(self) + child.set_pose_matrix_and_custom_props(self, settings) # Add bone children: for child in self.children: @@ -2888,7 +2903,7 @@ def load(operator, context, filepath="", continue if fbx_obj.props[-1] == b'Camera': assert(blen_data is None) - fbx_item[1] = blen_read_camera(fbx_tmpl, fbx_obj, global_scale) + fbx_item[1] = blen_read_camera(fbx_tmpl, fbx_obj, settings) _(); del _ # ---- @@ -2902,7 +2917,7 @@ def load(operator, context, filepath="", continue if fbx_obj.props[-1] == b'Light': assert(blen_data is None) - fbx_item[1] = blen_read_light(fbx_tmpl, fbx_obj, global_scale) + fbx_item[1] = blen_read_light(fbx_tmpl, fbx_obj, settings) _(); del _ # ---- @@ -2971,6 +2986,9 @@ def load(operator, context, filepath="", if fbx_sdata.id not in {b'Geometry', b'NodeAttribute'}: continue parent.bl_data = bl_data + if bl_data is None: + # If there's no bl_data, add the fbx_sdata so that it can be read when creating the bl_data/bone + parent.fbx_data_elem = fbx_sdata else: # set parent child.parent = parent