diff --git a/io_scene_fbx/__init__.py b/io_scene_fbx/__init__.py
index bae3e6cc3823f0a54bd6ed514aefd53a7c5ce3ca..bbdcd9ef36931b7ebaf29b980a146101327235db 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 09638c5d09d8a91fb0c8af0b716e75e66001302b..3ed0d7a51bf02da93e0124df29b8c8f830ec5a72 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 25f5759b816da377e4c4180025c267b68649b865..8de8e8f8daee0d033b025a9cbf0a619c13fb9c52 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 8b9a42cd462ddbed4baede76235019e4c2a2ade2..ee569f2825368b407be12657d34e8d856be30073 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)