diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py
index 9d0dfe41cd80e9a1fc20b9494d939bcbe712c899..9fa365478c0ce16377e7e6f28cff2a30037caac8 100644
--- a/io_scene_fbx/export_fbx_bin.py
+++ b/io_scene_fbx/export_fbx_bin.py
@@ -35,7 +35,7 @@ from itertools import zip_longest, chain
 
 import bpy
 import bpy_extras
-from bpy.types import Object, Bone, PoseBone
+from bpy.types import Object, Bone, PoseBone, DupliObject
 from mathutils import Vector, Matrix
 
 from . import encode_bin, data_types
@@ -272,6 +272,11 @@ def get_blender_empty_key(obj):
     return "|".join((get_blenderID_key(obj), "Empty"))
 
 
+def get_blender_dupli_key(dup):
+    """Return dupli's key (Model only)."""
+    return "|".join((get_blenderID_key(dup.object), "Dupli", "".join(str(i) for i in dup.persistent_id)))
+
+
 def get_blender_bone_key(armature, bone):
     """Return bone's keys (Model and NodeAttribute)."""
     key = "|".join((get_blenderID_key(armature), get_blenderID_key(bone)))
@@ -311,14 +316,14 @@ def get_blender_anim_layer_key(scene, ref_id):
     return get_blender_anim_id_base(scene, ref_id) + "|AnimLayer"
 
 
-def get_blender_anim_curve_node_key(scene, ref_id, ID, fbx_prop_name):
+def get_blender_anim_curve_node_key(scene, ref_id, obj_key, fbx_prop_name):
     """Return (stack/layer, ID, fbxprop) curve node key."""
-    return "|".join((get_blender_anim_id_base(scene, ref_id), get_blenderID_key(ID), fbx_prop_name, "AnimCurveNode"))
+    return "|".join((get_blender_anim_id_base(scene, ref_id), obj_key, fbx_prop_name, "AnimCurveNode"))
 
 
-def get_blender_anim_curve_key(scene, ref_id, ID, fbx_prop_name, fbx_prop_item_name):
+def get_blender_anim_curve_key(scene, ref_id, obj_key, fbx_prop_name, fbx_prop_item_name):
     """Return (stack/layer, ID, fbxprop, item) curve key."""
-    return "|".join((get_blender_anim_id_base(scene, ref_id), get_blenderID_key(ID), fbx_prop_name,
+    return "|".join((get_blender_anim_id_base(scene, ref_id), obj_key, fbx_prop_name,
                      fbx_prop_item_name, "AnimCurve"))
 
 
@@ -1004,6 +1009,15 @@ def fbx_template_def_animcurve(scene, settings, override_defaults=None, nbr_user
 
 
 ##### FBX objects generators. #####
+
+def dupli_list_create(obj, scene, settings='PREVIEW'):
+    # Sigh, why raise exception here? :/
+    try:
+        obj.dupli_list_create(scene, settings)
+    except:
+        pass
+
+
 def has_valid_parent(scene_data, obj):
     if isinstance(obj, PoseBone):
         obj = obj.bone
@@ -1027,14 +1041,15 @@ def fbx_object_matrix(scene_data, obj, armature=None, local_space=False, global_
     else in world space.
     Note local_space has precedence over global_space.
     If obj is a bone, and global_space is True, armature must be provided (it's the bone's armature object!).
+    obj can also be a DupliObject.
     Applies specific rotation to bones, lamps and cameras (conversion Blender -> FBX).
     """
     is_posebone = isinstance(obj, PoseBone)
     is_bone = is_posebone or isinstance(obj, Bone)
+    is_dupli = isinstance(obj, DupliObject)
     # Objects which are not bones and do not have any parent are *always* in global space (unless local_space is True!).
-    is_global = not local_space and (global_space or not (is_bone or has_valid_parent(scene_data, obj)))
+    is_global = not local_space and (global_space or not (is_bone or is_dupli or has_valid_parent(scene_data, obj)))
 
-    # Up till here, our matrix is in local space, time to bring it in its final desired space.
     if is_bone:
         bo = obj
         matrix = (bo.matrix if is_posebone else bo.matrix_local) * MAT_CONVERT_BONE
@@ -1047,7 +1062,13 @@ def fbx_object_matrix(scene_data, obj, armature=None, local_space=False, global_
             par_matrix = (bo.parent.matrix if is_posebone else bo.parent.matrix_local).copy()
             matrix = (par_matrix * MAT_CONVERT_BONE).inverted() * matrix
     else:
-        matrix = obj.matrix_local.copy()
+        if is_dupli:
+            parent = obj.id_data
+            # And here, we are in *world* space, go back to local (parent) space...
+            matrix = parent.matrix_world.inverted() * obj.matrix
+        else:
+            parent = obj.parent
+            matrix = obj.matrix_local.copy()
 
         # Lamps, and cameras need to be rotated (in local space!).
         if obj.type == 'LAMP':
@@ -1055,16 +1076,17 @@ def fbx_object_matrix(scene_data, obj, armature=None, local_space=False, global_
         elif obj.type == 'CAMERA':
             matrix = matrix * MAT_CONVERT_CAMERA
 
-        if obj.parent:
+        # 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 = obj.parent.matrix_world * matrix
-            elif use_bake_space_transform(scene_data, obj.parent):
+                matrix = parent.matrix_world * matrix
+            elif use_bake_space_transform(scene_data, parent):
                 # Blender's and FBX's local space of parent may differ if we use bake_space_transform...
                 # Apply parent's *Blender* local space...
-                matrix = obj.parent.matrix_local * matrix
+                matrix = parent.matrix_local * matrix
                 # ...and move it back into parent's *FBX* local space.
-                par_mat = fbx_object_matrix(scene_data, obj.parent, local_space=True)
+                par_mat = fbx_object_matrix(scene_data, parent, local_space=True)
                 matrix = par_mat.inverted() * matrix
 
     if use_bake_space_transform(scene_data, obj):
@@ -1914,6 +1936,11 @@ def fbx_data_object_elements(root, obj, scene_data):
     """
     obj_type = b"Null"  # default, sort of empty...
     tobj = obj
+    if isinstance(obj, DupliObject):
+        obj_key = scene_data.objects[tuple(obj.persistent_id)][0]
+        obj = obj.object
+    else:
+        obj_key = scene_data.objects[obj]
     if isinstance(obj, Bone):
         obj_type = b"LimbNode"
         # Get PoseBone for transformations!
@@ -1924,9 +1951,8 @@ def fbx_data_object_elements(root, obj, scene_data):
         obj_type = b"Light"
     elif (obj.type == 'CAMERA'):
         obj_type = b"Camera"
-    obj_key = scene_data.objects[obj]
     model = elem_data_single_int64(root, b"Model", get_fbxuid_from_key(obj_key))
-    model.add_string(fbx_name_class(obj.name.encode(), b"Model"))
+    model.add_string(fbx_name_class(obj_key.encode(), b"Model"))
     model.add_string(obj_type)
 
     elem_data_single_int32(model, b"Version", FBX_MODELS_VERSION)
@@ -2211,9 +2237,15 @@ def fbx_animations_objects_do(scene_data, ref_id, f_start, f_end, start_zero, ob
     Generate animation data (a single AnimStack) from objects, for a given frame range.
     """
     if objects is not None:
-        # Add bones!
-        objects |= {bo for vo in objects if (isinstance(vo, bpy.types.Object) and vo.type == 'ARMATURE')
-                       for bo in vo.data.bones}
+        # Add bones and duplis!
+        for obj in tuple(objects):
+            if not isinstance(obj, Object):
+                continue
+            if obj.type == 'ARMATURE':
+                objects |= set(vo.data.bones)
+            dupli_list_create(obj, scene, 'RENDER')
+            objects |= {tuple(dup.persistent_id) for dup in obj.dupli_list if tuple(dup.persistent_id) in scene_data.objects}
+            obj.dupli_list_clear()
     else:
         objects = scene_data.objects.keys()
     bake_step = scene_data.settings.bake_anim_step
@@ -2236,15 +2268,31 @@ def fbx_animations_objects_do(scene_data, ref_id, f_start, f_end, start_zero, ob
     while currframe < f_end:
         real_currframe = currframe - f_start if start_zero else currframe
         scene.frame_set(int(currframe), currframe - int(currframe))
+
+        duplis = {}
+        for obj in objects:
+            if not isinstance(obj, Object):
+                continue
+            dupli_list_create(obj, scene, 'RENDER')
+            duplis.update((tuple(dup.persistent_id), dup) for dup in obj.dupli_list if tuple(dup.persistent_id) in objects)
         for obj in objects:
             # Get PoseBone from bone...
-            tobj = bone_map[obj] if isinstance(obj, Bone) else obj
+            if isinstance(obj, Bone):
+                tobj = bone_map[obj]
+            # Get DupliObject from its pid...
+            elif isinstance(obj, tuple):
+                tobj = duplis[obj]
+            else:
+                tobj = obj
             # We compute baked loc/rot/scale for all objects (rot being euler-compat with previous value!).
             p_rot = p_rots.get(tobj, None)
             loc, rot, scale, _m, _mr = fbx_object_tx(scene_data, tobj, p_rot)
             p_rots[tobj] = rot
             tx = tuple(loc) + tuple(units_convert_iter(rot, "radian", "degree")) + tuple(scale)
             animdata[obj].append((real_currframe, tx, [False] * len(tx)))
+        for obj in objects:
+            if isinstance(obj, Object):
+                obj.dupli_list_clear()
         currframe += bake_step
 
     scene.frame_set(back_currframe, 0.0)
@@ -2263,6 +2311,10 @@ def fbx_animations_objects_do(scene_data, ref_id, f_start, f_end, start_zero, ob
                 if wrt:
                     curves[idx].append((currframe, val))
 
+        if isinstance(obj, tuple):
+            obj_key = scene_data.objects[obj][0]
+        else:
+            obj_key = scene_data.objects[obj]
         # Get PoseBone from bone...
         #tobj = bone_map[obj] if isinstance(obj, Bone) else obj
         #loc, rot, scale, _m, _mr = fbx_object_tx(scene_data, tobj)
@@ -2272,9 +2324,9 @@ def fbx_animations_objects_do(scene_data, ref_id, f_start, f_end, start_zero, ob
         final_keys = OrderedDict()
         for idx, c in enumerate(curves):
             fbx_group, fbx_gname, fbx_item = fbx_names[idx]
-            fbx_item_key = get_blender_anim_curve_key(scene, ref_id, obj, fbx_group, fbx_item)
+            fbx_item_key = get_blender_anim_curve_key(scene, ref_id, obj_key, fbx_group, fbx_item)
             if fbx_group not in final_keys:
-                fbx_group_key = get_blender_anim_curve_node_key(scene, ref_id, obj, fbx_group)
+                fbx_group_key = get_blender_anim_curve_node_key(scene, ref_id, obj_key, fbx_group)
                 final_keys[fbx_group] = (fbx_group_key, OrderedDict(), fbx_gname)
             final_keys[fbx_group][1][fbx_item] = (fbx_item_key, dtx[idx], c, True if len(c) > 1 else False)
         # And now, remove anim groups (i.e. groups of curves affecting a single FBX property) with no curve at all!
@@ -2464,13 +2516,20 @@ def fbx_data_from_scene(scene, settings):
         if use_org_data:
             data_meshes[obj] = (get_blenderID_key(obj.data), obj.data, False)
 
+    # Duplis...
+    for obj in tuple(objects.keys()):
+        dupli_list_create(obj, scene, 'RENDER')
+        for dup in obj.dupli_list:
+            objects[tuple(dup.persistent_id)] = (get_blender_dupli_key(dup), dup.object, obj)
+        obj.dupli_list_clear()
+
     # Armatures!
     data_bones = OrderedDict()
     data_deformers = OrderedDict()
     bones_to_posebones = dict()
     arm_parents = set()
     for obj in tuple(objects.keys()):
-        if obj.type not in {'ARMATURE'}:
+        if not (isinstance(obj, Object) and obj.type in {'ARMATURE'}):
             continue
         fbx_skeleton_from_armature(scene, settings, obj, objects, data_meshes, bones_to_posebones,
                                    data_bones, data_deformers, arm_parents)
@@ -2486,11 +2545,15 @@ def fbx_data_from_scene(scene, settings):
     #       *Should* work, as FBX always links its materials to Models (i.e. objects).
     #       XXX However, material indices would probably break...
     data_materials = OrderedDict()
-    for obj in objects:
+    for obj, obj_key in objects.items():
+        if isinstance(obj, tuple):
+            _dupli_key, real_obj, _par = obj_key
+        else:
+            real_obj = obj
         # Only meshes for now!
-        if not isinstance(obj, Object) or obj.type not in BLENDER_OBJECT_TYPES_MESHLIKE:
+        if not (isinstance(real_obj, Object) and real_obj.type in BLENDER_OBJECT_TYPES_MESHLIKE):
             continue
-        for mat_s in obj.material_slots:
+        for mat_s in real_obj.material_slots:
             mat = mat_s.material
             # Note theoretically, FBX supports any kind of materials, even GLSL shaders etc.
             # However, I doubt anything else than Lambert/Phong is really portable!
@@ -2638,7 +2701,13 @@ def fbx_data_from_scene(scene, settings):
                         par_key = objects[par]
                 else:
                     print("Sorry, “{}” parenting type is not supported".format(par_type))
-            connections.append((b"OO", get_fbxuid_from_key(obj_key), get_fbxuid_from_key(par_key), None))
+        elif isinstance(obj, tuple):
+            # Dupli instance...
+            obj_key, _obj, par = obj_key
+            par_key = objects[par]
+        else:
+            continue
+        connections.append((b"OO", get_fbxuid_from_key(obj_key), get_fbxuid_from_key(par_key), None))
 
     # Armature & Bone chains.
     for bo, (bo_key, _bo_data_key, arm) in data_bones.items():
@@ -2649,28 +2718,27 @@ def fbx_data_from_scene(scene, settings):
             continue
         connections.append((b"OO", get_fbxuid_from_key(bo_key), get_fbxuid_from_key(objects[par]), None))
 
-    # Empties
-    for empty_obj, empty_key in data_empties.items():
-        empty_obj_key = objects[empty_obj]
-        connections.append((b"OO", get_fbxuid_from_key(empty_key), get_fbxuid_from_key(empty_obj_key), None))
-
-    # Cameras
-    for obj_cam, cam_key in data_cameras.items():
-        cam_obj_key = objects[obj_cam]
-        connections.append((b"OO", get_fbxuid_from_key(cam_key), get_fbxuid_from_key(cam_obj_key), None))
-
     # Object data.
     for obj, obj_key in objects.items():
         if isinstance(obj, Bone):
             _bo_key, bo_data_key, _arm = data_bones[obj]
             assert(_bo_key == obj_key)
             connections.append((b"OO", get_fbxuid_from_key(bo_data_key), get_fbxuid_from_key(obj_key), None))
-        elif obj.type == 'LAMP':
-            lamp_key = data_lamps[obj.data]
-            connections.append((b"OO", get_fbxuid_from_key(lamp_key), get_fbxuid_from_key(obj_key), None))
-        elif obj.type in BLENDER_OBJECT_TYPES_MESHLIKE:
-            mesh_key, _me, _free = data_meshes[obj]
-            connections.append((b"OO", get_fbxuid_from_key(mesh_key), get_fbxuid_from_key(obj_key), None))
+        else:
+            if isinstance(obj, tuple):
+                obj_key, obj, _par = obj_key
+            if obj.type == 'LAMP':
+                lamp_key = data_lamps[obj.data]
+                connections.append((b"OO", get_fbxuid_from_key(lamp_key), get_fbxuid_from_key(obj_key), None))
+            elif obj.type == 'CAMERA':
+                cam_key = data_cameras[obj]
+                connections.append((b"OO", get_fbxuid_from_key(cam_key), get_fbxuid_from_key(obj_key), None))
+            elif obj.type == 'EMPTY':
+                empty_key = data_empties[obj.data]
+                connections.append((b"OO", get_fbxuid_from_key(empty_key), get_fbxuid_from_key(obj_key), None))
+            elif obj.type in BLENDER_OBJECT_TYPES_MESHLIKE:
+                mesh_key, _me, _free = data_meshes[obj]
+                connections.append((b"OO", get_fbxuid_from_key(mesh_key), get_fbxuid_from_key(obj_key), None))
 
     # Deformers (armature-to-geometry, only for meshes currently)...
     for arm, deformed_meshes in data_deformers.items():
@@ -2690,14 +2758,18 @@ def fbx_data_from_scene(scene, settings):
     _objs_indices = {}
     for mat, (mat_key, objs) in data_materials.items():
         for obj in objs:
-            obj_key = objects[obj]
-            connections.append((b"OO", get_fbxuid_from_key(mat_key), get_fbxuid_from_key(obj_key), None))
-            # Get index of this mat for this object.
-            # Mat indices for mesh faces are determined by their order in 'mat to ob' connections.
-            # Only mats for meshes currently...
-            me = obj.data
-            idx = _objs_indices[obj] = _objs_indices.get(obj, -1) + 1
-            mesh_mat_indices.setdefault(me, OrderedDict())[mat] = idx
+            if (isinstance(obj, tuple)):
+                obj_key, _obj, _par = objects[obj]
+                connections.append((b"OO", get_fbxuid_from_key(mat_key), get_fbxuid_from_key(obj_key), None))
+            else:
+                obj_key = objects[obj]
+                connections.append((b"OO", get_fbxuid_from_key(mat_key), get_fbxuid_from_key(obj_key), None))
+                # Get index of this mat for this object.
+                # Mat indices for mesh faces are determined by their order in 'mat to ob' connections.
+                # Only mats for meshes currently...
+                me = obj.data
+                idx = _objs_indices[obj] = _objs_indices.get(obj, -1) + 1
+                mesh_mat_indices.setdefault(me, OrderedDict())[mat] = idx
     del _objs_indices
 
     # Textures
@@ -2722,7 +2794,10 @@ def fbx_data_from_scene(scene, settings):
         alayer_id = get_fbxuid_from_key(alayer_key)
         connections.append((b"OO", alayer_id, astack_id, None))
         for obj, (alayer_key, acurvenodes) in astack.items():
-            obj_id = get_fbxuid_from_key(objects[obj])
+            if isinstance(obj, tuple):
+                obj_id = get_fbxuid_from_key(objects[obj][0])
+            else:
+                obj_id = get_fbxuid_from_key(objects[obj])
             # Animlayer -> animstack.
             # alayer_id = get_fbxuid_from_key(alayer_key)
             # connections.append((b"OO", alayer_id, astack_id, None))
@@ -2936,10 +3011,19 @@ def fbx_objects_elements(root, scene_data):
     del done_meshes
 
     for obj in scene_data.objects.keys():
+        if isinstance(obj, tuple):
+            continue
         fbx_data_object_elements(objects, obj, scene_data)
+        if isinstance(obj, Object):
+            dupli_list_create(obj, scene_data.scene, 'RENDER')
+            for dup in obj.dupli_list:
+                if tuple(dup.persistent_id) not in scene_data.objects:
+                    continue
+                fbx_data_object_elements(objects, dup, scene_data)
+            obj.dupli_list_clear()
 
     for obj in scene_data.objects.keys():
-        if not isinstance(obj, Object) or obj.type not in {'ARMATURE'}:
+        if not (isinstance(obj, Object) and obj.type in {'ARMATURE'}):
             continue
         fbx_data_armature_elements(objects, obj, scene_data)