Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Montagne <montagne29@wanadoo.fr>2018-09-21 16:12:27 +0300
committerBastien Montagne <montagne29@wanadoo.fr>2018-09-21 16:12:27 +0300
commit2bfab056d2655bddf3612dbe7644bce9ad1b581b (patch)
tree6e7b4b450c96c5414c7fae6c8515fb6bf5a8e89e /io_scene_fbx
parentb5328b13c54bdd3419d48373e3f54f22e2501396 (diff)
Initial port of FBX to 2.8.
Default cube scene exports and imports ok (besides missing features like nodal material handling). Anything else is either known broken, or yet to be tested. :P Note that I raised main number of addon version, so that we can keep track of smaller fixes that can be done in both 2.7x and 2.8 versions of the addon.
Diffstat (limited to 'io_scene_fbx')
-rw-r--r--io_scene_fbx/__init__.py107
-rw-r--r--io_scene_fbx/export_fbx_bin.py73
-rw-r--r--io_scene_fbx/fbx_utils.py109
-rw-r--r--io_scene_fbx/import_fbx.py130
4 files changed, 214 insertions, 205 deletions
diff --git a/io_scene_fbx/__init__.py b/io_scene_fbx/__init__.py
index bae3e6cc..bbdcd9ef 100644
--- a/io_scene_fbx/__init__.py
+++ b/io_scene_fbx/__init__.py
@@ -21,8 +21,8 @@
bl_info = {
"name": "FBX format",
"author": "Campbell Barton, Bastien Montagne, Jens Restemeier",
- "version": (3, 10, 0),
- "blender": (2, 79, 1),
+ "version": (4, 10, 0),
+ "blender": (2, 80, 0),
"location": "File > Import-Export",
"description": "FBX IO meshes, UV's, vertex colors, materials, textures, cameras, lamps and actions",
"warning": "",
@@ -67,12 +67,12 @@ class ImportFBX(bpy.types.Operator, ImportHelper, IOFBXOrientationHelper):
bl_label = "Import FBX"
bl_options = {'UNDO', 'PRESET'}
- directory = StringProperty()
+ directory: StringProperty()
filename_ext = ".fbx"
- filter_glob = StringProperty(default="*.fbx", options={'HIDDEN'})
+ filter_glob: StringProperty(default="*.fbx", options={'HIDDEN'})
- ui_tab = EnumProperty(
+ ui_tab: EnumProperty(
items=(('MAIN', "Main", "Main basic settings"),
('ARMATURE', "Armatures", "Armature-related settings"),
),
@@ -80,17 +80,17 @@ class ImportFBX(bpy.types.Operator, ImportHelper, IOFBXOrientationHelper):
description="Import options categories",
)
- use_manual_orientation = BoolProperty(
+ use_manual_orientation: BoolProperty(
name="Manual Orientation",
description="Specify orientation and scale, instead of using embedded data in FBX file",
default=False,
)
- global_scale = FloatProperty(
+ global_scale: FloatProperty(
name="Scale",
min=0.001, max=1000.0,
default=1.0,
)
- bake_space_transform = BoolProperty(
+ bake_space_transform: BoolProperty(
name="!EXPERIMENTAL! Apply Transform",
description="Bake space transform into object data, avoids getting unwanted rotations to objects when "
"target space is not aligned with Blender's space "
@@ -98,69 +98,69 @@ class ImportFBX(bpy.types.Operator, ImportHelper, IOFBXOrientationHelper):
default=False,
)
- use_custom_normals = BoolProperty(
+ use_custom_normals: BoolProperty(
name="Import Normals",
description="Import custom normals, if available (otherwise Blender will recompute them)",
default=True,
)
- use_image_search = BoolProperty(
+ use_image_search: BoolProperty(
name="Image Search",
description="Search subdirs for any associated images (WARNING: may be slow)",
default=True,
)
- use_alpha_decals = BoolProperty(
+ use_alpha_decals: BoolProperty(
name="Alpha Decals",
description="Treat materials with alpha as decals (no shadow casting)",
default=False,
)
- decal_offset = FloatProperty(
+ decal_offset: FloatProperty(
name="Decal Offset",
description="Displace geometry of alpha meshes",
min=0.0, max=1.0,
default=0.0,
)
- use_anim = BoolProperty(
+ use_anim: BoolProperty(
name="Import Animation",
description="Import FBX animation",
default=True,
)
- anim_offset = FloatProperty(
+ anim_offset: FloatProperty(
name="Animation Offset",
description="Offset to apply to animation during import, in frames",
default=1.0,
)
- use_custom_props = BoolProperty(
+ use_custom_props: BoolProperty(
name="Import User Properties",
description="Import user properties as custom properties",
default=True,
)
- use_custom_props_enum_as_string = BoolProperty(
+ use_custom_props_enum_as_string: BoolProperty(
name="Import Enums As Strings",
description="Store enumeration values as strings",
default=True,
)
- ignore_leaf_bones = BoolProperty(
+ ignore_leaf_bones: BoolProperty(
name="Ignore Leaf Bones",
description="Ignore the last bone at the end of each chain (used to mark the length of the previous bone)",
default=False,
)
- force_connect_children = BoolProperty(
+ force_connect_children: BoolProperty(
name="Force Connect Children",
description="Force connection of children bones to their parent, even if their computed head/tail "
"positions do not match (can be useful with pure-joints-type armatures)",
default=False,
)
- automatic_bone_orientation = BoolProperty(
+ automatic_bone_orientation: BoolProperty(
name="Automatic Bone Orientation",
description="Try to align the major bone axis with the bone children",
default=False,
)
- primary_bone_axis = EnumProperty(
+ primary_bone_axis: EnumProperty(
name="Primary Bone Axis",
items=(('X', "X Axis", ""),
('Y', "Y Axis", ""),
@@ -171,7 +171,7 @@ class ImportFBX(bpy.types.Operator, ImportHelper, IOFBXOrientationHelper):
),
default='Y',
)
- secondary_bone_axis = EnumProperty(
+ secondary_bone_axis: EnumProperty(
name="Secondary Bone Axis",
items=(('X', "X Axis", ""),
('Y', "Y Axis", ""),
@@ -183,7 +183,7 @@ class ImportFBX(bpy.types.Operator, ImportHelper, IOFBXOrientationHelper):
default='X',
)
- use_prepost_rot = BoolProperty(
+ use_prepost_rot: BoolProperty(
name="Use Pre/Post Rotation",
description="Use pre/post rotation from FBX transform (you may have to disable that in some cases)",
default=True,
@@ -228,7 +228,8 @@ class ImportFBX(bpy.types.Operator, ImportHelper, IOFBXOrientationHelper):
def execute(self, context):
keywords = self.as_keywords(ignore=("filter_glob", "directory", "ui_tab"))
- keywords["use_cycles"] = (context.scene.render.engine == 'CYCLES')
+ # XXX TODO get rid of this, EEVEE/Cycles use same nodal system...
+ keywords["use_cycles"] = True #(context.scene.render.engine == 'CYCLES')
from . import import_fbx
return import_fbx.load(self, context, **keywords)
@@ -241,12 +242,12 @@ class ExportFBX(bpy.types.Operator, ExportHelper, IOFBXOrientationHelper):
bl_options = {'UNDO', 'PRESET'}
filename_ext = ".fbx"
- filter_glob = StringProperty(default="*.fbx", options={'HIDDEN'})
+ filter_glob: StringProperty(default="*.fbx", options={'HIDDEN'})
# List of operator properties, the attributes will be assigned
# to the class instance from the operator settings before calling.
- ui_tab = EnumProperty(
+ ui_tab: EnumProperty(
items=(('MAIN', "Main", "Main basic settings"),
('GEOMETRY', "Geometries", "Geometry-related settings"),
('ARMATURE', "Armatures", "Armature-related settings"),
@@ -256,24 +257,24 @@ class ExportFBX(bpy.types.Operator, ExportHelper, IOFBXOrientationHelper):
description="Export options categories",
)
- use_selection = BoolProperty(
+ use_selection: BoolProperty(
name="Selected Objects",
description="Export selected objects on visible layers",
default=False,
)
- global_scale = FloatProperty(
+ global_scale: FloatProperty(
name="Scale",
description="Scale all data (Some importers do not support scaled armatures!)",
min=0.001, max=1000.0,
soft_min=0.01, soft_max=1000.0,
default=1.0,
)
- apply_unit_scale = BoolProperty(
+ apply_unit_scale: BoolProperty(
name="Apply Unit",
description="Take into account current Blender units settings (if unset, raw Blender Units values are used as-is)",
default=True,
)
- apply_scale_options = EnumProperty(
+ apply_scale_options: EnumProperty(
items=(('FBX_SCALE_NONE', "All Local",
"Apply custom scaling and units scaling to each object transformation, FBX scale remains at 1.0"),
('FBX_SCALE_UNITS', "FBX Units Scale",
@@ -288,7 +289,7 @@ class ExportFBX(bpy.types.Operator, ExportHelper, IOFBXOrientationHelper):
"(Blender uses FBX scale to detect units on import, "
"but many other applications do not handle the same way)",
)
- bake_space_transform = BoolProperty(
+ bake_space_transform: BoolProperty(
name="!EXPERIMENTAL! Apply Transform",
description="Bake space transform into object data, avoids getting unwanted rotations to objects when "
"target space is not aligned with Blender's space "
@@ -296,7 +297,7 @@ class ExportFBX(bpy.types.Operator, ExportHelper, IOFBXOrientationHelper):
default=False,
)
- object_types = EnumProperty(
+ object_types: EnumProperty(
name="Object Types",
options={'ENUM_FLAG'},
items=(('EMPTY', "Empty", ""),
@@ -310,18 +311,18 @@ class ExportFBX(bpy.types.Operator, ExportHelper, IOFBXOrientationHelper):
default={'EMPTY', 'CAMERA', 'LIGHT', 'ARMATURE', 'MESH', 'OTHER'},
)
- use_mesh_modifiers = BoolProperty(
+ use_mesh_modifiers: BoolProperty(
name="Apply Modifiers",
description="Apply modifiers to mesh objects (except Armature ones) - "
"WARNING: prevents exporting shape keys",
default=True,
)
- use_mesh_modifiers_render = BoolProperty(
+ use_mesh_modifiers_render: BoolProperty(
name="Use Modifiers Render Setting",
description="Use render settings when applying modifiers to mesh objects",
default=True,
)
- mesh_smooth_type = EnumProperty(
+ mesh_smooth_type: EnumProperty(
name="Smoothing",
items=(('OFF', "Normals Only", "Export only normals instead of writing edge or face smoothing data"),
('FACE', "Face", "Write face smoothing"),
@@ -331,29 +332,29 @@ class ExportFBX(bpy.types.Operator, ExportHelper, IOFBXOrientationHelper):
"(prefer 'Normals Only' option if your target importer understand split normals)",
default='OFF',
)
- use_mesh_edges = BoolProperty(
+ use_mesh_edges: BoolProperty(
name="Loose Edges",
description="Export loose edges (as two-vertices polygons)",
default=False,
)
- use_tspace = BoolProperty(
+ use_tspace: BoolProperty(
name="Tangent Space",
description="Add binormal and tangent vectors, together with normal they form the tangent space "
"(will only work correctly with tris/quads only meshes!)",
default=False,
)
- use_custom_props = BoolProperty(
+ use_custom_props: BoolProperty(
name="Custom Properties",
description="Export custom properties",
default=False,
)
- add_leaf_bones = BoolProperty(
+ add_leaf_bones: BoolProperty(
name="Add Leaf Bones",
description="Append a final bone to the end of each chain to specify last bone length "
"(use this when you intend to edit the armature from exported data)",
default=True # False for commit!
)
- primary_bone_axis = EnumProperty(
+ primary_bone_axis: EnumProperty(
name="Primary Bone Axis",
items=(('X', "X Axis", ""),
('Y', "Y Axis", ""),
@@ -364,7 +365,7 @@ class ExportFBX(bpy.types.Operator, ExportHelper, IOFBXOrientationHelper):
),
default='Y',
)
- secondary_bone_axis = EnumProperty(
+ secondary_bone_axis: EnumProperty(
name="Secondary Bone Axis",
items=(('X', "X Axis", ""),
('Y', "Y Axis", ""),
@@ -375,12 +376,12 @@ class ExportFBX(bpy.types.Operator, ExportHelper, IOFBXOrientationHelper):
),
default='X',
)
- use_armature_deform_only = BoolProperty(
+ use_armature_deform_only: BoolProperty(
name="Only Deform Bones",
description="Only write deforming bones (and non-deforming ones when they have deforming children)",
default=False,
)
- armature_nodetype = EnumProperty(
+ armature_nodetype: EnumProperty(
name="Armature FBXNode Type",
items=(('NULL', "Null", "'Null' FBX node, similar to Blender's Empty (default)"),
('ROOT', "Root", "'Root' FBX node, supposed to be the root of chains of bones..."),
@@ -391,43 +392,43 @@ class ExportFBX(bpy.types.Operator, ExportHelper, IOFBXOrientationHelper):
"perfectly in Blender...)",
default='NULL',
)
- bake_anim = BoolProperty(
+ bake_anim: BoolProperty(
name="Baked Animation",
description="Export baked keyframe animation",
default=True,
)
- bake_anim_use_all_bones = BoolProperty(
+ bake_anim_use_all_bones: BoolProperty(
name="Key All Bones",
description="Force exporting at least one key of animation for all bones "
"(needed with some target applications, like UE4)",
default=True,
)
- bake_anim_use_nla_strips = BoolProperty(
+ bake_anim_use_nla_strips: BoolProperty(
name="NLA Strips",
description="Export each non-muted NLA strip as a separated FBX's AnimStack, if any, "
"instead of global scene animation",
default=True,
)
- bake_anim_use_all_actions = BoolProperty(
+ bake_anim_use_all_actions: BoolProperty(
name="All Actions",
description="Export each action as a separated FBX's AnimStack, instead of global scene animation "
"(note that animated objects will get all actions compatible with them, "
"others will get no animation at all)",
default=True,
)
- bake_anim_force_startend_keying = BoolProperty(
+ bake_anim_force_startend_keying: BoolProperty(
name="Force Start/End Keying",
description="Always add a keyframe at start and end of actions for animated channels",
default=True,
)
- bake_anim_step = FloatProperty(
+ bake_anim_step: FloatProperty(
name="Sampling Rate",
description="How often to evaluate animated values (in frames)",
min=0.01, max=100.0,
soft_min=0.1, soft_max=10.0,
default=1.0,
)
- bake_anim_simplify_factor = FloatProperty(
+ bake_anim_simplify_factor: FloatProperty(
name="Simplify",
description="How much to simplify baked values (0.0 to disable, the higher the more simplified)",
min=0.0, max=100.0, # No simplification to up to 10% of current magnitude tolerance.
@@ -435,24 +436,24 @@ class ExportFBX(bpy.types.Operator, ExportHelper, IOFBXOrientationHelper):
default=1.0, # default: min slope: 0.005, max frame step: 10.
)
path_mode = path_reference_mode
- embed_textures = BoolProperty(
+ embed_textures: BoolProperty(
name="Embed Textures",
description="Embed textures in FBX binary file (only for \"Copy\" path mode!)",
default=False,
)
- batch_mode = EnumProperty(
+ batch_mode: EnumProperty(
name="Batch Mode",
items=(('OFF', "Off", "Active scene to file"),
('SCENE', "Scene", "Each scene as a file"),
('GROUP', "Group", "Each group as a file"),
),
)
- use_batch_own_dir = BoolProperty(
+ use_batch_own_dir: BoolProperty(
name="Batch Own Dir",
description="Create a dir for each exported file",
default=True,
)
- use_metadata = BoolProperty(
+ use_metadata: BoolProperty(
name="Use Metadata",
default=True,
options={'HIDDEN'},
diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py
index 09638c5d..3ed0d7a5 100644
--- a/io_scene_fbx/export_fbx_bin.py
+++ b/io_scene_fbx/export_fbx_bin.py
@@ -585,8 +585,8 @@ def fbx_data_light_elements(root, lamp, scene_data):
if lamp.type not in {'HEMI'}:
if lamp.type not in {'SUN', 'AREA'}:
decay_type = FBX_LIGHT_DECAY_TYPES[lamp.falloff_type]
- do_light = (not lamp.use_only_shadow) and (lamp.use_specular or lamp.use_diffuse)
- do_shadow = lamp.shadow_method not in {'NOSHADOW'}
+ do_light = True
+ do_shadow = lamp.use_shadow
shadow_color = lamp.shadow_color
light = elem_data_single_int64(root, b"NodeAttribute", get_fbx_uuid_from_key(light_key))
@@ -629,8 +629,8 @@ def fbx_data_camera_elements(root, cam_obj, scene_data):
# Real data now, good old camera!
# Object transform info.
loc, rot, scale, matrix, matrix_rot = cam_obj.fbx_object_tx(scene_data)
- up = matrix_rot * Vector((0.0, 1.0, 0.0))
- to = matrix_rot * Vector((0.0, 0.0, -1.0))
+ up = matrix_rot @ Vector((0.0, 1.0, 0.0))
+ to = matrix_rot @ Vector((0.0, 0.0, -1.0))
# Render settings.
# TODO We could export much more...
render = scene_data.scene.render
@@ -1206,7 +1206,8 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
def check_skip_material(mat):
"""Simple helper to check whether we actually support exporting that material or not"""
- return mat.type not in {'SURFACE'}
+ ### TODO Fix node-to-simpleshader issue...
+ return True or mat.type not in {'SURFACE'}
def fbx_data_material_elements(root, mat, scene_data):
@@ -1215,7 +1216,7 @@ def fbx_data_material_elements(root, mat, scene_data):
"""
ambient_color = (0.0, 0.0, 0.0)
if scene_data.data_world:
- ambient_color = next(iter(scene_data.data_world.keys())).ambient_color
+ ambient_color = next(iter(scene_data.data_world.keys())).color
mat_key, _objs = scene_data.data_materials[mat]
skip_mat = check_skip_material(mat)
@@ -1497,7 +1498,7 @@ def fbx_data_armature_elements(root, arm_obj, scene_data):
# http://area.autodesk.com/forum/autodesk-fbx/fbx-sdk/why-the-values-return-
# by-fbxcluster-gettransformmatrix-x-not-same-with-the-value-in-ascii-fbx-file/
elem_data_single_float64_array(fbx_clstr, b"Transform",
- matrix4_to_array(mat_world_bones[bo_obj].inverted_safe() * mat_world_obj))
+ matrix4_to_array(mat_world_bones[bo_obj].inverted_safe() @ mat_world_obj))
elem_data_single_float64_array(fbx_clstr, b"TransformLink", matrix4_to_array(mat_world_bones[bo_obj]))
elem_data_single_float64_array(fbx_clstr, b"TransformAssociateModel", matrix4_to_array(mat_world_arm))
@@ -1849,9 +1850,9 @@ def fbx_generate_leaf_bones(settings, data_bones):
bone_length = (parent.bdata.tail_local - parent.bdata.head_local).length
matrix = Matrix.Translation((0, bone_length, 0))
if settings.bone_correction_matrix_inv:
- matrix = settings.bone_correction_matrix_inv * matrix
+ matrix = settings.bone_correction_matrix_inv @ matrix
if settings.bone_correction_matrix:
- matrix = matrix * settings.bone_correction_matrix
+ matrix = matrix @ settings.bone_correction_matrix
leaf_bones.append((node_name, parent_uuid, node_uuid, attr_uuid, matrix, hide, size))
return leaf_bones
@@ -1864,6 +1865,7 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
bake_step = scene_data.settings.bake_anim_step
simplify_fac = scene_data.settings.bake_anim_simplify_factor
scene = scene_data.scene
+ depsgraph = scene_data.depsgraph
force_keying = scene_data.settings.bake_anim_use_all_bones
force_sek = scene_data.settings.bake_anim_force_startend_keying
@@ -1874,11 +1876,9 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
continue
if ob_obj.type == 'ARMATURE':
objects |= {bo_obj for bo_obj in ob_obj.bones if bo_obj in scene_data.objects}
- ob_obj.dupli_list_create(scene, 'RENDER')
- for dp_obj in ob_obj.dupli_list:
+ for dp_obj in ob_obj.dupli_list_gen(depsgraph):
if dp_obj in scene_data.objects:
objects.add(dp_obj)
- ob_obj.dupli_list_clear()
else:
objects = scene_data.objects
@@ -1920,10 +1920,10 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
currframe = f_start
while currframe <= f_end:
real_currframe = currframe - f_start if start_zero else currframe
- scene.frame_set(int(currframe), currframe - int(currframe))
+ scene.frame_set(int(currframe), subframe=currframe - int(currframe))
- for ob_obj in animdata_ob:
- ob_obj.dupli_list_create(scene, 'RENDER')
+ for dp_obj in ob_obj.dupli_list_gen(depsgraph):
+ pass # Merely updating dupli matrix of ObjectWrapper...
for ob_obj, (anim_loc, anim_rot, anim_scale) in animdata_ob.items():
# We compute baked loc/rot/scale for all objects (rot being euler-compat with previous value!).
p_rot = p_rots.get(ob_obj, None)
@@ -1932,15 +1932,13 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
anim_loc.add_keyframe(real_currframe, loc)
anim_rot.add_keyframe(real_currframe, tuple(convert_rad_to_deg_iter(rot)))
anim_scale.add_keyframe(real_currframe, scale)
- for ob_obj in objects:
- ob_obj.dupli_list_clear()
for anim_shape, me, shape in animdata_shapes.values():
anim_shape.add_keyframe(real_currframe, (shape.value * 100.0,))
for anim_camera, camera in animdata_cameras.values():
anim_camera.add_keyframe(real_currframe, (camera.lens,))
currframe += bake_step
- scene.frame_set(back_currframe, 0.0)
+ scene.frame_set(back_currframe, subframe=0.0)
animations = OrderedDict()
@@ -2045,7 +2043,7 @@ def fbx_animations(scene_data):
add_anim(animations, animated,
fbx_animations_do(scene_data, strip, strip.frame_start, strip.frame_end, True, force_keep=True))
strip.mute = True
- scene.frame_set(scene.frame_current, 0.0)
+ scene.frame_set(scene.frame_current, subframe=0.0)
for strip in strips:
strip.mute = False
@@ -2074,7 +2072,7 @@ def fbx_animations(scene_data):
'lock_location', 'lock_rotation', 'lock_rotation_w', 'lock_rotations_4d', 'lock_scale',
'tag', 'layers', 'select', 'track_axis', 'up_axis', 'active_material', 'active_material_index',
'matrix_parent_inverse', 'empty_display_type', 'empty_display_size', 'empty_image_offset', 'pass_index',
- 'color', 'hide', 'hide_select', 'hide_render', 'use_slow_parent', 'slow_parent_offset',
+ 'color', 'hide_viewport', 'hide_select', 'hide_render', 'use_slow_parent', 'slow_parent_offset',
'use_extra_recalc_object', 'use_extra_recalc_data', 'dupli_type', 'use_dupli_frames_speed',
'use_dupli_vertices_rotation', 'use_dupli_faces_scale', 'dupli_faces_scale', 'dupli_group',
'dupli_frames_start', 'dupli_frames_end', 'dupli_frames_on', 'dupli_frames_off',
@@ -2124,7 +2122,7 @@ def fbx_animations(scene_data):
pbo.matrix_basis = mat.copy()
ob.animation_data.action = org_act
restore_object(ob, ob_copy)
- scene.frame_set(scene.frame_current, 0.0)
+ scene.frame_set(scene.frame_current, subframe=0.0)
if pbones_matrices is not ...:
for pbo, mat in zip(ob.pose.bones, pbones_matrices):
@@ -2132,19 +2130,19 @@ def fbx_animations(scene_data):
ob.animation_data.action = org_act
bpy.data.objects.remove(ob_copy)
- scene.frame_set(scene.frame_current, 0.0)
+ scene.frame_set(scene.frame_current, subframe=0.0)
# Global (containing everything) animstack, only if not exporting NLA strips and/or all actions.
if not scene_data.settings.bake_anim_use_nla_strips and not scene_data.settings.bake_anim_use_all_actions:
add_anim(animations, animated, fbx_animations_do(scene_data, None, scene.frame_start, scene.frame_end, False))
# Be sure to update all matrices back to org state!
- scene.frame_set(scene.frame_current, 0.0)
+ scene.frame_set(scene.frame_current, subframe=0.0)
return animations, animated, frame_start, frame_end
-def fbx_data_from_scene(scene, settings):
+def fbx_data_from_scene(scene, depsgraph, settings):
"""
Do some pre-processing over scene's data...
"""
@@ -2166,12 +2164,10 @@ def fbx_data_from_scene(scene, settings):
ob_obj = ObjectWrapper(ob)
objects[ob_obj] = None
# Duplis...
- ob_obj.dupli_list_create(scene, 'RENDER')
- for dp_obj in ob_obj.dupli_list:
+ for dp_obj in ob_obj.dupli_list_gen(depsgraph):
if dp_obj.type not in dp_objtypes:
continue
objects[dp_obj] = None
- ob_obj.dupli_list_clear()
perfmon.step("FBX export prepare: Wrapping Data (lamps, cameras, empties)...")
@@ -2375,7 +2371,7 @@ def fbx_data_from_scene(scene, settings):
# Kind of hack, we need a temp scene_data for object's space handling to bake animations...
tmp_scdata = FBXExportData(
None, None, None,
- settings, scene, objects, None, None, 0.0, 0.0,
+ settings, scene, depsgraph, objects, None, None, 0.0, 0.0,
data_empties, data_lights, data_cameras, data_meshes, None,
data_bones, data_leaf_bones, data_deformers_skin, data_deformers_shape,
data_world, data_materials, data_textures, data_videos,
@@ -2593,7 +2589,7 @@ def fbx_data_from_scene(scene, settings):
return FBXExportData(
templates, templates_users, connections,
- settings, scene, objects, animations, animated, frame_start, frame_end,
+ settings, scene, depsgraph, objects, animations, animated, frame_start, frame_end,
data_empties, data_lights, data_cameras, data_meshes, mesh_mat_indices,
data_bones, data_leaf_bones, data_deformers_skin, data_deformers_shape,
data_world, data_materials, data_textures, data_videos,
@@ -2827,12 +2823,10 @@ def fbx_objects_elements(root, scene_data):
if ob_obj.is_dupli:
continue
fbx_data_object_elements(objects, ob_obj, scene_data)
- ob_obj.dupli_list_create(scene_data.scene, 'RENDER')
- for dp_obj in ob_obj.dupli_list:
+ for dp_obj in ob_obj.dupli_list_gen(scene_data.depsgraph):
if dp_obj not in scene_data.objects:
continue
fbx_data_object_elements(objects, dp_obj, scene_data)
- ob_obj.dupli_list_clear()
perfmon.step("FBX export fetch remaining...")
@@ -2897,7 +2891,7 @@ def fbx_takes_elements(root, scene_data):
# ##### "Main" functions. #####
# This func can be called with just the filepath
-def save_single(operator, scene, filepath="",
+def save_single(operator, scene, depsgraph, filepath="",
global_matrix=Matrix(),
apply_unit_scale=False,
global_scale=1.0,
@@ -2943,12 +2937,12 @@ def save_single(operator, scene, filepath="",
# Default Blender unit is equivalent to meter, while FBX one is centimeter...
unit_scale = units_blender_to_fbx_factor(scene) if apply_unit_scale else 100.0
if apply_scale_options == 'FBX_SCALE_NONE':
- global_matrix = Matrix.Scale(unit_scale * global_scale, 4) * global_matrix
+ global_matrix = Matrix.Scale(unit_scale * global_scale, 4) @ global_matrix
unit_scale = 1.0
elif apply_scale_options == 'FBX_SCALE_UNITS':
- global_matrix = Matrix.Scale(global_scale, 4) * global_matrix
+ global_matrix = Matrix.Scale(global_scale, 4) @ global_matrix
elif apply_scale_options == 'FBX_SCALE_CUSTOM':
- global_matrix = Matrix.Scale(unit_scale, 4) * global_matrix
+ global_matrix = Matrix.Scale(unit_scale, 4) @ global_matrix
unit_scale = global_scale
else: # if apply_scale_options == 'FBX_SCALE_ALL':
unit_scale = global_scale * unit_scale
@@ -3004,7 +2998,7 @@ def save_single(operator, scene, filepath="",
start_time = time.process_time()
# Generate some data about exported scene...
- scene_data = fbx_data_from_scene(scene, settings)
+ scene_data = fbx_data_from_scene(scene, depsgraph, settings)
root = elem_empty(None, b"") # Root element has no id, as it is not saved per se!
@@ -3098,7 +3092,7 @@ def save(operator, context,
ret = None
- active_object = context.scene.objects.active
+ active_object = context.view_layer.objects.active
org_mode = None
if active_object and active_object.mode != 'OBJECT' and bpy.ops.object.mode_set.poll():
@@ -3112,8 +3106,9 @@ def save(operator, context,
else:
kwargs_mod["context_objects"] = context.scene.objects
- ret = save_single(operator, context.scene, filepath, **kwargs_mod)
+ ret = save_single(operator, context.scene, context.depsgraph, filepath, **kwargs_mod)
else:
+ return # TODO Update for 2.8
fbxpath = filepath
prefix = os.path.basename(fbxpath)
diff --git a/io_scene_fbx/fbx_utils.py b/io_scene_fbx/fbx_utils.py
index 25f5759b..8de8e8f8 100644
--- a/io_scene_fbx/fbx_utils.py
+++ b/io_scene_fbx/fbx_utils.py
@@ -30,7 +30,7 @@ from itertools import zip_longest, chain
import bpy
import bpy_extras
-from bpy.types import Object, Bone, PoseBone, DupliObject
+from bpy.types import Object, Bone, PoseBone, DepsgraphObjectInstance
from mathutils import Vector, Matrix
from . import encode_bin, data_types
@@ -271,14 +271,14 @@ def similar_values_iter(v1, v2, e=1e-6):
def vcos_transformed_gen(raw_cos, m=None):
# Note: we could most likely get much better performances with numpy, but will leave this as TODO for now.
gen = zip(*(iter(raw_cos),) * 3)
- return gen if m is None else (m * Vector(v) for v in gen)
+ return gen if m is None else (m @ Vector(v) for v in gen)
def nors_transformed_gen(raw_nors, m=None):
# Great, now normals are also expected 4D!
# XXX Back to 3D normals for now!
# gen = zip(*(iter(raw_nors),) * 3 + (_infinite_gen(1.0),))
gen = zip(*(iter(raw_nors),) * 3)
- return gen if m is None else (m * Vector(v) for v in gen)
+ return gen if m is None else (m @ Vector(v) for v in gen)
# ##### UIDs code. #####
@@ -856,7 +856,7 @@ class AnimationCurveNodeWrapper:
# ##### FBX objects generators. #####
-# FBX Model-like data (i.e. Blender objects, dupliobjects and bones) are wrapped in ObjectWrapper.
+# FBX Model-like data (i.e. Blender objects, depsgraph instances and bones) are wrapped in ObjectWrapper.
# This allows us to have a (nearly) same code FBX-wise for all those types.
# The wrapper tries to stay as small as possible, by mostly using callbacks (property(get...))
# to actual Blender data it contains.
@@ -870,9 +870,12 @@ class MetaObjectWrapper(type):
dup_mat = None
if isinstance(bdata, Object):
key = get_blenderID_key(bdata)
- elif isinstance(bdata, DupliObject):
- key = "|".join((get_blenderID_key((bdata.id_data, bdata.object)), cls._get_dup_num_id(bdata)))
- dup_mat = bdata.matrix.copy()
+ elif isinstance(bdata, DepsgraphObjectInstance):
+ if bdata.is_instance:
+ key = "|".join((get_blenderID_key((bdata.parent, bdata.object_instance)), cls._get_dup_num_id(bdata)))
+ dup_mat = bdata.matrix_world.copy()
+ else:
+ key = get_blenderID_key(bdata.object)
else: # isinstance(bdata, (Bone, PoseBone)):
if isinstance(bdata, PoseBone):
bdata = armature.data.bones[bdata.name]
@@ -883,9 +886,9 @@ class MetaObjectWrapper(type):
cache = cls._cache = {}
instance = cache.get(key)
if instance is not None:
- # Duplis hack: since duplis are not persistent in Blender (we have to re-create them to get updated
+ # Duplis hack: since dupli instances are not persistent in Blender (we have to re-create them to get updated
# info like matrix...), we *always* need to reset that matrix when calling ObjectWrapper() (all
- # other data is supposed valid during whole cache live, so we can skip resetting it).
+ # other data is supposed valid during whole cache live span, so we can skip resetting it).
instance._dupli_matrix = dup_mat
return instance
@@ -902,7 +905,7 @@ class ObjectWrapper(metaclass=MetaObjectWrapper):
This class provides a same common interface for all (FBX-wise) object-like elements:
* Blender Object
* Blender Bone and PoseBone
- * Blender DupliObject
+ * Blender DepsgraphObjectInstance (for dulis).
Note since a same Blender object might be 'mapped' to several FBX models (esp. with duplis),
we need to use a key to identify each.
"""
@@ -918,24 +921,42 @@ class ObjectWrapper(metaclass=MetaObjectWrapper):
@staticmethod
def _get_dup_num_id(bdata):
- return ".".join(str(i) for i in bdata.persistent_id if i != 2147483647)
+ INVALID_IDS = {2147483647, 0}
+ pids = tuple(bdata.persistent_id)
+ idx_valid = 0
+ prev_i = ...
+ for idx, i in enumerate(pids[::-1]):
+ if i not in INVALID_IDS or (idx == len(pids) and i == 0 and prev_i != 0):
+ idx_valid = len(pids) - idx
+ break
+ prev_i = i
+ return ".".join(str(i) for i in pids[:idx_valid])
def __init__(self, bdata, armature=None):
"""
- bdata might be an Object, DupliObject, Bone or PoseBone.
+ bdata might be an Object (deprecated), DepsgraphObjectInstance, Bone or PoseBone.
If Bone or PoseBone, armature Object must be provided.
"""
- if isinstance(bdata, Object):
+ # Note: DepsgraphObjectInstance are purely runtime data, they become invalid as soon as we step to the next item!
+ # Hence we have to immediately copy *all* needed data...
+ if isinstance(bdata, Object): # DEPRECATED
self._tag = 'OB'
self.name = get_blenderID_name(bdata)
self.bdata = bdata
self._ref = None
- elif isinstance(bdata, DupliObject):
- self._tag = 'DP'
- self.name = "|".join((get_blenderID_name((bdata.id_data, bdata.object)),
- "Dupli", self._get_dup_num_id(bdata)))
- self.bdata = bdata.object
- self._ref = bdata.id_data
+ elif isinstance(bdata, DepsgraphObjectInstance):
+ if bdata.is_instance:
+ # Note that dupli instance matrix is set by meta-class initialization.
+ self._tag = 'DP'
+ self.name = "|".join((get_blenderID_name((bdata.parent, bdata.object)),
+ "Dupli", self._get_dup_num_id(bdata)))
+ self.bdata = bdata.object
+ self._ref = bdata.parent
+ else:
+ self._tag = 'OB'
+ self.name = get_blenderID_name(bdata)
+ self.bdata = bdata
+ self._ref = None
else: # isinstance(bdata, (Bone, PoseBone)):
if isinstance(bdata, PoseBone):
bdata = armature.data.bones[bdata.name]
@@ -956,8 +977,9 @@ class ObjectWrapper(metaclass=MetaObjectWrapper):
return get_fbx_uuid_from_key(self.key)
fbx_uuid = property(get_fbx_uuid)
+ # XXX Not sure how much that’s useful now... :/
def get_hide(self):
- return self.bdata.hide
+ return self.bdata.hide_viewport if self._tag in {'OB', 'DP'} else self.bdata.hide
hide = property(get_hide)
def get_parent(self):
@@ -974,7 +996,7 @@ class ObjectWrapper(metaclass=MetaObjectWrapper):
# Mere object parenting.
return ObjectWrapper(self.bdata.parent)
elif self._tag == 'DP':
- return ObjectWrapper(self.bdata.parent or self._ref)
+ return ObjectWrapper(self._ref)
else: # self._tag == 'BO'
return ObjectWrapper(self.bdata.parent, self._ref) or ObjectWrapper(self._ref)
parent = property(get_parent)
@@ -983,12 +1005,12 @@ class ObjectWrapper(metaclass=MetaObjectWrapper):
if self._tag == 'OB':
return self.bdata.matrix_local.copy()
elif self._tag == 'DP':
- return self._ref.matrix_world.inverted_safe() * self._dupli_matrix
+ return self._ref.matrix_world.inverted_safe() @ self._dupli_matrix
else: # 'BO', current pose
# PoseBone.matrix is in armature space, bring in back in real local one!
par = self.bdata.parent
par_mat_inv = self._ref.pose.bones[par.name].matrix.inverted_safe() if par else Matrix()
- return par_mat_inv * self._ref.pose.bones[self.bdata.name].matrix
+ return par_mat_inv @ self._ref.pose.bones[self.bdata.name].matrix
matrix_local = property(get_matrix_local)
def get_matrix_global(self):
@@ -997,7 +1019,7 @@ class ObjectWrapper(metaclass=MetaObjectWrapper):
elif self._tag == 'DP':
return self._dupli_matrix
else: # 'BO', current pose
- return self._ref.matrix_world * self._ref.pose.bones[self.bdata.name].matrix
+ return self._ref.matrix_world @ self._ref.pose.bones[self.bdata.name].matrix
matrix_global = property(get_matrix_global)
def get_matrix_rest_local(self):
@@ -1005,14 +1027,14 @@ class ObjectWrapper(metaclass=MetaObjectWrapper):
# Bone.matrix_local is in armature space, bring in back in real local one!
par = self.bdata.parent
par_mat_inv = par.matrix_local.inverted_safe() if par else Matrix()
- return par_mat_inv * self.bdata.matrix_local
+ return par_mat_inv @ self.bdata.matrix_local
else:
return self.matrix_local.copy()
matrix_rest_local = property(get_matrix_rest_local)
def get_matrix_rest_global(self):
if self._tag == 'BO':
- return self._ref.matrix_world * self.bdata.matrix_local
+ return self._ref.matrix_world @ self.bdata.matrix_local
else:
return self.matrix_global.copy()
matrix_rest_global = property(get_matrix_rest_global)
@@ -1065,41 +1087,41 @@ class ObjectWrapper(metaclass=MetaObjectWrapper):
if self._tag == 'BO':
# If we have a bone parent we need to undo the parent correction.
if not is_global and scene_data.settings.bone_correction_matrix_inv and parent and parent.is_bone:
- matrix = scene_data.settings.bone_correction_matrix_inv * matrix
+ matrix = scene_data.settings.bone_correction_matrix_inv @ matrix
# Apply the bone correction.
if scene_data.settings.bone_correction_matrix:
- matrix = matrix * scene_data.settings.bone_correction_matrix
+ matrix = matrix @ scene_data.settings.bone_correction_matrix
elif self.bdata.type == 'LIGHT':
- matrix = matrix * MAT_CONVERT_LIGHT
+ matrix = matrix @ MAT_CONVERT_LIGHT
elif self.bdata.type == 'CAMERA':
- matrix = matrix * MAT_CONVERT_CAMERA
+ matrix = matrix @ MAT_CONVERT_CAMERA
if self._tag in {'DP', 'OB'} and parent:
if parent._tag == 'BO':
# In bone parent case, we get transformation in **bone tip** space (sigh).
# Have to bring it back into bone root, which is FBX expected value.
- matrix = Matrix.Translation((0, (parent.bdata.tail - parent.bdata.head).length, 0)) * matrix
+ matrix = Matrix.Translation((0, (parent.bdata.tail - parent.bdata.head).length, 0)) @ matrix
# Our matrix is in local space, time to bring it in its final desired space.
if parent:
if is_global:
# Move matrix to global Blender space.
- matrix = (parent.matrix_rest_global if rest else parent.matrix_global) * matrix
+ matrix = (parent.matrix_rest_global if rest else parent.matrix_global) @ matrix
elif parent.use_bake_space_transform(scene_data):
# Blender's and FBX's local space of parent may differ if we use bake_space_transform...
# Apply parent's *Blender* local space...
- matrix = (parent.matrix_rest_local if rest else parent.matrix_local) * matrix
+ matrix = (parent.matrix_rest_local if rest else parent.matrix_local) @ matrix
# ...and move it back into parent's *FBX* local space.
par_mat = parent.fbx_object_matrix(scene_data, rest=rest, local_space=True)
- matrix = par_mat.inverted_safe() * matrix
+ matrix = par_mat.inverted_safe() @ matrix
if self.use_bake_space_transform(scene_data):
# If we bake the transforms we need to post-multiply inverse global transform.
# This means that the global transform will not apply to children of this transform.
- matrix = matrix * scene_data.settings.global_matrix_inv
+ matrix = matrix @ scene_data.settings.global_matrix_inv
if is_global:
# In any case, pre-multiply the global matrix to get it in FBX global space!
- matrix = scene_data.settings.global_matrix * matrix
+ matrix = scene_data.settings.global_matrix @ matrix
return matrix
@@ -1164,19 +1186,10 @@ class ObjectWrapper(metaclass=MetaObjectWrapper):
return True
# #### Duplis...
- def dupli_list_create(self, scene, settings='PREVIEW'):
+ def dupli_list_gen(self, depsgraph):
if self._tag == 'OB' and self.bdata.is_duplicator:
- self.bdata.dupli_list_create(scene, settings)
-
- def dupli_list_clear(self):
- if self._tag == 'OB'and self.bdata.is_duplicator:
- self.bdata.dupli_list_clear()
-
- def get_dupli_list(self):
- if self._tag == 'OB'and self.bdata.is_duplicator:
- return (ObjectWrapper(dup) for dup in self.bdata.dupli_list)
+ return (ObjectWrapper(dup) for dup in depsgraph.object_instances if dup.parent == self.bdata)
return ()
- dupli_list = property(get_dupli_list)
def fbx_name_class(name, cls):
@@ -1213,7 +1226,7 @@ FBXExportSettings = namedtuple("FBXExportSettings", (
# * animations.
FBXExportData = namedtuple("FBXExportData", (
"templates", "templates_users", "connections",
- "settings", "scene", "objects", "animations", "animated", "frame_start", "frame_end",
+ "settings", "scene", "depsgraph", "objects", "animations", "animated", "frame_start", "frame_end",
"data_empties", "data_lights", "data_cameras", "data_meshes", "mesh_mat_indices",
"data_bones", "data_leaf_bones", "data_deformers_skin", "data_deformers_shape",
"data_world", "data_materials", "data_textures", "data_videos",
diff --git a/io_scene_fbx/import_fbx.py b/io_scene_fbx/import_fbx.py
index 8b9a42cd..ee569f28 100644
--- a/io_scene_fbx/import_fbx.py
+++ b/io_scene_fbx/import_fbx.py
@@ -369,7 +369,7 @@ def blen_read_custom_properties(fbx_obj, blen_obj, settings):
def blen_read_object_transform_do(transform_data):
# This is a nightmare. FBX SDK uses Maya way to compute the transformation matrix of a node - utterly simple:
#
- # WorldTransform = ParentWorldTransform * T * Roff * Rp * Rpre * R * Rpost * Rp-1 * Soff * Sp * S * Sp-1
+ # WorldTransform = ParentWorldTransform @ T @ Roff @ Rp @ Rpre @ R @ Rpost @ Rp-1 @ Soff @ Sp @ S @ Sp-1
#
# Where all those terms are 4 x 4 matrices that contain:
# WorldTransform: Transformation matrix of the node in global space.
@@ -389,7 +389,7 @@ def blen_read_object_transform_do(transform_data):
# But it was still too simple, and FBX notion of compatibility is... quite specific. So we also have to
# support 3DSMax way:
#
- # WorldTransform = ParentWorldTransform * T * R * S * OT * OR * OS
+ # WorldTransform = ParentWorldTransform @ T @ R @ S @ OT @ OR @ OS
#
# Where all those terms are 4 x 4 matrices that contain:
# WorldTransform: Transformation matrix of the node in global space
@@ -414,7 +414,7 @@ def blen_read_object_transform_do(transform_data):
# rotation
to_rot = lambda rot, rot_ord: Euler(convert_deg_to_rad_iter(rot), rot_ord).to_matrix().to_4x4()
- lcl_rot = to_rot(transform_data.rot, transform_data.rot_ord) * transform_data.rot_alt_mat
+ lcl_rot = to_rot(transform_data.rot, transform_data.rot_ord) @ transform_data.rot_alt_mat
pre_rot = to_rot(transform_data.pre_rot, transform_data.rot_ord)
pst_rot = to_rot(transform_data.pst_rot, transform_data.rot_ord)
geom_rot = to_rot(transform_data.geom_rot, transform_data.rot_ord)
@@ -431,21 +431,21 @@ def blen_read_object_transform_do(transform_data):
geom_scale[0][0], geom_scale[1][1], geom_scale[2][2] = transform_data.geom_sca
base_mat = (
- lcl_translation *
- rot_ofs *
- rot_piv *
- pre_rot *
- lcl_rot *
- pst_rot *
- rot_piv.inverted_safe() *
- sca_ofs *
- sca_piv *
- lcl_scale *
+ lcl_translation @
+ rot_ofs @
+ rot_piv @
+ pre_rot @
+ lcl_rot @
+ pst_rot @
+ rot_piv.inverted_safe() @
+ sca_ofs @
+ sca_piv @
+ lcl_scale @
sca_piv.inverted_safe()
)
- geom_mat = geom_loc * geom_rot * geom_scale
+ geom_mat = geom_loc @ geom_rot @ geom_scale
# We return mat without 'geometric transforms' too, because it is to be used for children, sigh...
- return (base_mat * geom_mat, base_mat, geom_mat)
+ return (base_mat @ geom_mat, base_mat, geom_mat)
# XXX This might be weak, now that we can add vgroups from both bones and shapes, name collisions become
@@ -661,19 +661,19 @@ def blen_read_animations_action_item(action, item, cnodes, fps, anim_offset):
# compensate for changes in the local matrix during processing
if item.anim_compensation_matrix:
- mat = mat * item.anim_compensation_matrix
+ mat = mat @ item.anim_compensation_matrix
# apply pre- and post matrix
# post-matrix will contain any correction for lights, camera and bone orientation
# pre-matrix will contain any correction for a parent's correction matrix or the global matrix
if item.pre_matrix:
- mat = item.pre_matrix * mat
+ mat = item.pre_matrix @ mat
if item.post_matrix:
- mat = mat * item.post_matrix
+ mat = mat @ item.post_matrix
# And now, remove that rest pose matrix from current mat (also in parent space).
if restmat_inv:
- mat = restmat_inv * mat
+ mat = restmat_inv @ mat
# Now we have a virtual matrix of transform from AnimCurves, we can insert keyframes!
loc, rot, sca = mat.decompose()
@@ -1006,8 +1006,7 @@ def blen_read_geom_layer_uv(fbx_obj, mesh):
fbx_layer_data = elem_prop_first(elem_find_first(fbx_layer, b'UV'))
fbx_layer_index = elem_prop_first(elem_find_first(fbx_layer, b'UVIndex'))
- uv_tex = mesh.uv_textures.new(name=fbx_layer_name)
- uv_lay = mesh.uv_layers[-1]
+ uv_lay = mesh.uv_layers.new(name=fbx_layer_name)
blen_data = uv_lay.data
# some valid files omit this data
@@ -1165,7 +1164,7 @@ def blen_read_geom(fbx_tmpl, fbx_obj, settings):
if geom_mat_co is not None:
def _vcos_transformed_gen(raw_cos, m=None):
# Note: we could most likely get much better performances with numpy, but will leave this as TODO for now.
- return chain(*(m * Vector(v) for v in zip(*(iter(raw_cos),) * 3)))
+ return chain(*(m @ Vector(v) for v in zip(*(iter(raw_cos),) * 3)))
fbx_verts = array.array(fbx_verts.typecode, _vcos_transformed_gen(fbx_verts, geom_mat_co))
if fbx_verts is None:
@@ -1242,7 +1241,7 @@ def blen_read_geom(fbx_tmpl, fbx_obj, settings):
ok_normals = blen_read_geom_layer_normal(fbx_obj, mesh)
else:
def nortrans(v):
- return geom_mat_no * Vector(v)
+ return geom_mat_no @ Vector(v)
ok_normals = blen_read_geom_layer_normal(fbx_obj, mesh, nortrans)
mesh.validate(clean_customdata=False) # *Very* important to not remove lnors here!
@@ -1498,7 +1497,7 @@ def blen_read_light(fbx_tmpl, fbx_obj, global_scale):
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.shadow_method = ('RAY_SHADOW' if elem_props_get_bool(fbx_props, b'CastShadow', True) else 'NOSHADOW')
+ lamp.use_shadow = elem_props_get_bool(fbx_props, b'CastShadow', True)
lamp.shadow_color = elem_props_get_color_rgb(fbx_props, b'ShadowColor', (0.0, 0.0, 0.0))
return lamp
@@ -1612,7 +1611,7 @@ class FbxImportHelperNode:
self.pre_matrix = settings.global_matrix
if parent_correction_inv:
- self.pre_matrix = parent_correction_inv * (self.pre_matrix if self.pre_matrix else Matrix())
+ self.pre_matrix = parent_correction_inv @ (self.pre_matrix if self.pre_matrix else Matrix())
correction_matrix = None
@@ -1705,7 +1704,7 @@ class FbxImportHelperNode:
self.post_matrix = correction_matrix
if self.do_bake_transform(settings):
- self.post_matrix = settings.global_matrix_inv * (self.post_matrix if self.post_matrix else Matrix())
+ self.post_matrix = settings.global_matrix_inv @ (self.post_matrix if self.post_matrix else Matrix())
# process children
correction_matrix_inv = correction_matrix.inverted_safe() if correction_matrix else None
@@ -1782,29 +1781,29 @@ class FbxImportHelperNode:
def get_world_matrix_as_parent(self):
matrix = self.parent.get_world_matrix_as_parent() if self.parent else Matrix()
if self.matrix_as_parent:
- matrix = matrix * self.matrix_as_parent
+ matrix = matrix @ self.matrix_as_parent
return matrix
def get_world_matrix(self):
matrix = self.parent.get_world_matrix_as_parent() if self.parent else Matrix()
if self.matrix:
- matrix = matrix * self.matrix
+ matrix = matrix @ self.matrix
return matrix
def get_matrix(self):
matrix = self.matrix if self.matrix else Matrix()
if self.pre_matrix:
- matrix = self.pre_matrix * matrix
+ matrix = self.pre_matrix @ matrix
if self.post_matrix:
- matrix = matrix * self.post_matrix
+ matrix = matrix @ self.post_matrix
return matrix
def get_bind_matrix(self):
matrix = self.bind_matrix if self.bind_matrix else Matrix()
if self.pre_matrix:
- matrix = self.pre_matrix * matrix
+ matrix = self.pre_matrix @ matrix
if self.post_matrix:
- matrix = matrix * self.post_matrix
+ matrix = matrix @ self.post_matrix
return matrix
def make_bind_pose_local(self, parent_matrix=None):
@@ -1812,13 +1811,13 @@ class FbxImportHelperNode:
parent_matrix = Matrix()
if self.bind_matrix:
- bind_matrix = parent_matrix.inverted_safe() * self.bind_matrix
+ bind_matrix = parent_matrix.inverted_safe() @ self.bind_matrix
else:
bind_matrix = self.matrix.copy() if self.matrix else None
self.bind_matrix = bind_matrix
if bind_matrix:
- parent_matrix = parent_matrix * bind_matrix
+ parent_matrix = parent_matrix @ bind_matrix
for child in self.children:
child.make_bind_pose_local(parent_matrix)
@@ -1838,8 +1837,8 @@ class FbxImportHelperNode:
child.collect_skeleton_meshes(meshes)
for m in meshes:
old_matrix = m.matrix
- m.matrix = armature_matrix_inv * m.get_world_matrix()
- m.anim_compensation_matrix = old_matrix.inverted_safe() * m.matrix
+ m.matrix = armature_matrix_inv @ m.get_world_matrix()
+ m.anim_compensation_matrix = old_matrix.inverted_safe() @ m.matrix
m.is_global_animation = True
m.parent = self
self.meshes = meshes
@@ -1914,7 +1913,7 @@ class FbxImportHelperNode:
bone.tail = bone_tail
# And rotate/move it to its final "rest pose".
- bone_matrix = parent_matrix * self.get_bind_matrix().normalized()
+ bone_matrix = parent_matrix @ self.get_bind_matrix().normalized()
bone.matrix = bone_matrix
@@ -1927,7 +1926,7 @@ class FbxImportHelperNode:
if child.is_leaf and force_connect_children:
# Arggggggggggggggggg! We do not want to create this bone, but we need its 'virtual head' location
# to orient current one!!!
- child_head = (bone_matrix * child.get_bind_matrix().normalized()).translation
+ 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,
@@ -1958,7 +1957,7 @@ class FbxImportHelperNode:
# Misc Attributes
obj.color[0:3] = elem_props_get_color_rgb(fbx_props, b'Color', (0.8, 0.8, 0.8))
- obj.hide = not bool(elem_props_get_visibility(fbx_props, b'Visibility', 1.0))
+ obj.hide_viewport = not bool(elem_props_get_visibility(fbx_props, b'Visibility', 1.0))
obj.matrix_basis = self.get_matrix()
@@ -1967,12 +1966,12 @@ class FbxImportHelperNode:
return obj
- def build_skeleton_children(self, fbx_tmpl, settings, scene):
+ def build_skeleton_children(self, fbx_tmpl, settings, scene, view_layer):
if self.is_bone:
for child in self.children:
if child.ignore:
continue
- child.build_skeleton_children(fbx_tmpl, settings, scene)
+ child.build_skeleton_children(fbx_tmpl, settings, scene, view_layer)
return None
else:
# child is not a bone
@@ -1984,11 +1983,11 @@ class FbxImportHelperNode:
for child in self.children:
if child.ignore:
continue
- child.build_skeleton_children(fbx_tmpl, settings, scene)
+ child.build_skeleton_children(fbx_tmpl, settings, scene, view_layer)
# instance in scene
- obj_base = scene.objects.link(obj)
- obj_base.select = True
+ view_layer.collections.active.collection.objects.link(obj)
+ obj.select_set('Select')
return obj
@@ -2007,7 +2006,7 @@ class FbxImportHelperNode:
# Blender attaches to the end of a bone, while FBX attaches to the start.
# bone_child_matrix corrects for that.
if child.pre_matrix:
- child.pre_matrix = self.bone_child_matrix * child.pre_matrix
+ child.pre_matrix = self.bone_child_matrix @ child.pre_matrix
else:
child.pre_matrix = self.bone_child_matrix
@@ -2027,7 +2026,7 @@ class FbxImportHelperNode:
def set_pose_matrix(self, arm):
pose_bone = arm.bl_obj.pose.bones[self.bl_bone]
- pose_bone.matrix_basis = self.get_bind_matrix().inverted_safe() * self.get_matrix()
+ pose_bone.matrix_basis = self.get_bind_matrix().inverted_safe() @ self.get_matrix()
for child in self.children:
if child.ignore:
@@ -2094,7 +2093,7 @@ class FbxImportHelperNode:
if child.is_bone and not child.ignore:
child.set_bone_weights()
- def build_hierarchy(self, fbx_tmpl, settings, scene):
+ def build_hierarchy(self, fbx_tmpl, settings, scene, view_layer):
if self.is_armature:
# create when linking since we need object data
elem_name_utf8 = self.fbx_name
@@ -2114,15 +2113,15 @@ class FbxImportHelperNode:
blen_read_custom_properties(self.fbx_elem, arm, settings)
# instance in scene
- obj_base = scene.objects.link(arm)
- obj_base.select = True
+ view_layer.collections.active.collection.objects.link(arm)
+ obj.select_set('Select')
# Add bones:
# Switch to Edit mode.
scene.objects.active = arm
- is_hidden = arm.hide
- arm.hide = False # Can't switch to Edit mode hidden objects...
+ is_hidden = arm.hide_viewport
+ arm.hide_viewport = False # Can't switch to Edit mode hidden objects...
bpy.ops.object.mode_set(mode='EDIT')
for child in self.children:
@@ -2133,7 +2132,7 @@ class FbxImportHelperNode:
bpy.ops.object.mode_set(mode='OBJECT')
- arm.hide = is_hidden
+ arm.hide_viewport = is_hidden
# Set pose matrix
for child in self.children:
@@ -2146,7 +2145,7 @@ class FbxImportHelperNode:
for child in self.children:
if child.ignore:
continue
- child_obj = child.build_skeleton_children(fbx_tmpl, settings, scene)
+ child_obj = child.build_skeleton_children(fbx_tmpl, settings, scene, view_layer)
return arm
elif self.fbx_elem and not self.is_bone:
@@ -2154,16 +2153,16 @@ class FbxImportHelperNode:
# walk through children
for child in self.children:
- child.build_hierarchy(fbx_tmpl, settings, scene)
+ child.build_hierarchy(fbx_tmpl, settings, scene, view_layer)
# instance in scene
- obj_base = scene.objects.link(obj)
- obj_base.select = True
+ view_layer.collections.active.collection.objects.link(obj)
+ obj.select_set('SELECT')
return obj
else:
for child in self.children:
- child.build_hierarchy(fbx_tmpl, settings, scene)
+ child.build_hierarchy(fbx_tmpl, settings, scene, view_layer)
return None
@@ -2192,16 +2191,16 @@ class FbxImportHelperNode:
# which we obviously cannot do in Blender. :/
if amat is None:
amat = self.bind_matrix
- amat = settings.global_matrix * (Matrix() if amat is None else amat)
+ amat = settings.global_matrix @ (Matrix() if amat is None else amat)
if self.matrix_geom:
- amat = amat * self.matrix_geom
- mmat = settings.global_matrix * mmat
+ amat = amat @ self.matrix_geom
+ mmat = settings.global_matrix @ mmat
if mesh.matrix_geom:
- mmat = mmat * mesh.matrix_geom
+ mmat = mmat @ mesh.matrix_geom
# Now that we have armature and mesh in there (global) bind 'state' (matrix),
# we can compute inverse parenting matrix of the mesh.
- me_obj.matrix_parent_inverse = amat.inverted_safe() * mmat * me_obj.matrix_basis.inverted_safe()
+ me_obj.matrix_parent_inverse = amat.inverted_safe() @ mmat @ me_obj.matrix_basis.inverted_safe()
mod = mesh.bl_obj.modifiers.new(arm.name, 'ARMATURE')
mod.object = arm
@@ -2325,6 +2324,7 @@ def load(operator, context, filepath="",
material_decals = None
scene = context.scene
+ view_layer = context.view_layer
# #### Get some info from GlobalSettings.
@@ -2350,7 +2350,7 @@ def load(operator, context, filepath="",
elem_props_get_integer(fbx_settings_props, b'CoordAxisSign', 1))
axis_key = (axis_up, axis_forward, axis_coord)
axis_up, axis_forward = {v: k for k, v in RIGHT_HAND_AXES.items()}.get(axis_key, ('Z', 'Y'))
- global_matrix = (Matrix.Scale(global_scale, 4) *
+ global_matrix = (Matrix.Scale(global_scale, 4) @
axis_conversion(from_forward=axis_forward, from_up=axis_up).to_4x4())
# To cancel out unwanted rotation/scale on nodes.
@@ -2682,7 +2682,7 @@ def load(operator, context, filepath="",
armature_matrix = tx_arm
if tx_bone:
- mesh_matrix = tx_bone * mesh_matrix
+ mesh_matrix = tx_bone @ mesh_matrix
helper_node.bind_matrix = tx_bone # overwrite the bind matrix
# Get the meshes driven by this cluster: (Shouldn't that be only one?)
@@ -2725,7 +2725,7 @@ def load(operator, context, filepath="",
root_helper.find_correction_matrix(settings)
# build the Object/Armature/Bone hierarchy
- root_helper.build_hierarchy(fbx_tmpl, settings, scene)
+ root_helper.build_hierarchy(fbx_tmpl, settings, scene, view_layer)
# Link the Object/Armature/Bone hierarchy
root_helper.link_hierarchy(fbx_tmpl, settings, scene)