diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm
index 5d6d8e4a6..4063824e5 100644
--- a/lib/Slic3r/GUI.pm
+++ b/lib/Slic3r/GUI.pm
@@ -41,16 +41,17 @@ use Wx::Event qw(EVT_IDLE EVT_COMMAND EVT_MENU);
 use base 'Wx::App';
 
 use constant FILE_WILDCARDS => {
-    known   => 'Known files (*.stl, *.obj, *.amf, *.xml, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.prusa;*.PRUSA',
+    known   => 'Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA',
     stl     => 'STL files (*.stl)|*.stl;*.STL',
     obj     => 'OBJ files (*.obj)|*.obj;*.OBJ',
     amf     => 'AMF files (*.amf)|*.amf;*.AMF;*.xml;*.XML',
+    threemf => '3MF files (*.3mf)|*.3mf;*.3MF',
     prusa   => 'Prusa Control files (*.prusa)|*.prusa;*.PRUSA',
     ini     => 'INI files *.ini|*.ini;*.INI',
     gcode   => 'G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC',
     svg     => 'SVG files *.svg|*.svg;*.SVG',
 };
-use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf prusa)};
+use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf threemf prusa)};
 
 # Datadir provided on the command line.
 our $datadir;
diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm
index c91c59ea7..3b82ee157 100644
--- a/lib/Slic3r/GUI/MainFrame.pm
+++ b/lib/Slic3r/GUI/MainFrame.pm
@@ -374,7 +374,7 @@ sub quick_slice {
         # select input file
         my $input_file;
         if (!$params{reslice}) {
-            my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF/PRUSA):', 
+            my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):', 
                 wxTheApp->{app_config}->get_last_dir, "", 
                 &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
             if ($dialog->ShowModal != wxID_OK) {
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index 10d65475c..a5a98d649 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -2063,8 +2063,8 @@ sub OnDropFiles {
     # stop scalars leaking on older perl
     # https://rt.perl.org/rt3/Public/Bug/Display.html?id=70602
     @_ = ();
-    # only accept STL, OBJ and AMF files
-    return 0 if grep !/\.(?:[sS][tT][lL]|[oO][bB][jJ]|[aA][mM][fF](?:\.[xX][mM][lL])?|[pP][rR][uU][sS][aA])$/, @$filenames;
+    # only accept STL, OBJ, AMF and 3MF files
+    return 0 if grep !/\.(?:[sS][tT][lL]|[oO][bB][jJ]|[aA][mM][fF]|[3][mM][fF](?:\.[xX][mM][lL])?|[pP][rR][uU][sS][aA])$/, @$filenames;
     $self->{window}->load_files($filenames);
 }
 
diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index 4c1f2bb8c..fbddc31e6 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -74,6 +74,8 @@ add_library(libslic3r STATIC
     ${LIBDIR}/libslic3r/Fill/FillRectilinear3.hpp
     ${LIBDIR}/libslic3r/Flow.cpp
     ${LIBDIR}/libslic3r/Flow.hpp
+    ${LIBDIR}/libslic3r/Format/3MF.cpp
+    ${LIBDIR}/libslic3r/Format/3MF.hpp
     ${LIBDIR}/libslic3r/Format/AMF.cpp
     ${LIBDIR}/libslic3r/Format/AMF.hpp
     ${LIBDIR}/libslic3r/Format/OBJ.cpp
diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp
new file mode 100644
index 000000000..6057e1c11
--- /dev/null
+++ b/xs/src/libslic3r/Format/3mf.cpp
@@ -0,0 +1,990 @@
+#include "../libslic3r.h"
+#include "../Model.hpp"
+
+#include "3mf.hpp"
+
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/split.hpp>
+
+#include <expat.h>
+#include <eigen/dense>
+#include <miniz/miniz_zip.h>
+
+const std::string MODEL_FOLDER = "3d\\";
+const std::string MODEL_EXTENSION = ".model";
+
+const char* MODEL_TAG = "model";
+const char* RESOURCES_TAG = "resources";
+const char* OBJECT_TAG = "object";
+const char* MESH_TAG = "mesh";
+const char* VERTICES_TAG = "vertices";
+const char* VERTEX_TAG = "vertex";
+const char* TRIANGLES_TAG = "triangles";
+const char* TRIANGLE_TAG = "triangle";
+const char* COMPONENTS_TAG = "components";
+const char* COMPONENT_TAG = "component";
+const char* BUILD_TAG = "build";
+const char* ITEM_TAG = "item";
+
+const char* UNIT_ATTR = "unit";
+const char* NAME_ATTR = "name";
+const char* TYPE_ATTR = "type";
+const char* ID_ATTR = "id";
+const char* X_ATTR = "x";
+const char* Y_ATTR = "y";
+const char* Z_ATTR = "z";
+const char* V1_ATTR = "v1";
+const char* V2_ATTR = "v2";
+const char* V3_ATTR = "v3";
+const char* OBJECTID_ATTR = "objectid";
+const char* TRANSFORM_ATTR = "transform";
+
+const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
+const char* VALID_OBJECT_TYPES[] =
+{
+    "model"
+};
+
+const unsigned int INVALID_OBJECT_TYPES_COUNT = 4;
+const char* INVALID_OBJECT_TYPES[] =
+{
+    "solidsupport",
+    "support",
+    "surface",
+    "other"
+};
+
+typedef Eigen::Matrix<float, 4, 4, Eigen::RowMajor> Matrix4x4;
+
+const char* get_attribute_value_charptr(const char** attributes, unsigned int attributes_size, const char* attribute_key)
+{
+    if ((attributes == nullptr) || (attributes_size == 0) || (attributes_size % 2 != 0) || (attribute_key == nullptr))
+        return nullptr;
+
+    for (unsigned int a = 0; a < attributes_size; a += 2)
+    {
+        if (::strcmp(attributes[a], attribute_key) == 0)
+            return attributes[a + 1];
+    }
+
+    return nullptr;
+}
+
+std::string get_attribute_value_string(const char** attributes, unsigned int attributes_size, const char* attribute_key)
+{
+    const char* text = get_attribute_value_charptr(attributes, attributes_size, attribute_key);
+    return (text != nullptr) ? text : "";
+}
+
+float get_attribute_value_float(const char** attributes, unsigned int attributes_size, const char* attribute_key)
+{
+    const char* text = get_attribute_value_charptr(attributes, attributes_size, attribute_key);
+    return (text != nullptr) ? (float)::atof(text) : 0.0f;
+}
+
+int get_attribute_value_int(const char** attributes, unsigned int attributes_size, const char* attribute_key)
+{
+    const char* text = get_attribute_value_charptr(attributes, attributes_size, attribute_key);
+    return (text != nullptr) ? ::atoi(text) : 0;
+}
+
+Matrix4x4 get_matrix_from_string(const std::string& mat_str)
+{
+    if (mat_str.empty())
+        // empty string means default identity matrix
+        return Matrix4x4::Identity();
+
+    std::vector<std::string> mat_elements_str;
+    boost::split(mat_elements_str, mat_str, boost::is_any_of(" "), boost::token_compress_on);
+
+    unsigned int size = (unsigned int)mat_elements_str.size();
+    if (size != 12)
+        // invalid data, return identity matrix
+        return Matrix4x4::Identity();
+
+    Matrix4x4 ret = Matrix4x4::Identity();
+    unsigned int i = 0;
+    // matrices are stored into 3mf files as 4x3
+    // we need to transpose them
+    for (unsigned int c = 0; c < 4; ++c)
+    {
+        for (unsigned int r = 0; r < 3; ++r)
+        {
+            ret(r, c) = (float)::atof(mat_elements_str[i++].c_str());
+        }
+    }
+    return ret;
+}
+
+float get_unit_factor(const std::string& unit)
+{
+    const char* text = unit.c_str();
+
+    if (::strcmp(text, "micron") == 0)
+        return 0.001f;
+    else if (::strcmp(text, "centimeter") == 0)
+        return 10.0f;
+    else if (::strcmp(text, "inch") == 0)
+        return 25.4f;
+    else if (::strcmp(text, "foot") == 0)
+        return 304.8f;
+    else if (::strcmp(text, "meter") == 0)
+        return 1000.0f;
+    else
+        // default "millimeters" (see specification)
+        return 1.0f;
+}
+
+bool is_valid_object_type(const std::string& type)
+{
+    // if the type is empty defaults to "model" (see specification)
+    if (type.empty())
+        return true;
+
+    for (unsigned int i = 0; i < VALID_OBJECT_TYPES_COUNT; ++i)
+    {
+        if (::strcmp(type.c_str(), VALID_OBJECT_TYPES[i]) == 0)
+            return true;
+    }
+
+    return false;
+}
+
+namespace Slic3r {
+
+    class _3MF_Importer
+    {
+        struct Component
+        {
+            int object_id;
+            Matrix4x4 matrix;
+
+            explicit Component(int object_id);
+            Component(int object_id, const Matrix4x4& matrix);
+        };
+
+        typedef std::vector<Component> ComponentsList;
+
+        struct CurrentObject
+        {
+            struct Geometry
+            {
+                std::vector<float> vertices;
+                std::vector<unsigned int> triangles;
+
+                bool empty();
+                void reset();
+            };
+
+            int id;
+            Geometry geometry;
+            ModelObject* object;
+            TriangleMesh mesh;
+            ComponentsList components;
+
+            CurrentObject();
+
+            void reset();
+        };
+
+        struct Instance
+        {
+            ModelInstance* instance;
+            Matrix4x4 matrix;
+
+            Instance(ModelInstance* instance, const Matrix4x4& matrix);
+        };
+
+        typedef std::map<int, ModelObject*> IdToModelObjectMap;
+        typedef std::map<int, ComponentsList> IdToAliasesMap;
+        typedef std::vector<Instance> InstancesList;
+
+        XML_Parser m_xml_parser;
+        Model* m_model;
+        float m_unit_factor;
+        CurrentObject m_curr_object;
+        IdToModelObjectMap m_objects;
+        IdToAliasesMap m_objects_aliases;
+        InstancesList m_instances;
+        std::vector<std::string> m_errors;
+
+    public:
+        _3MF_Importer();
+        ~_3MF_Importer();
+
+        bool load_model_from_file(const std::string& filename, Model& model);
+
+        const std::vector<std::string>& get_errors() const;
+
+    private:
+        void _destroy_xml_parser();
+        void _stop_xml_parser();
+
+        bool _load_model_from_file_miniz(const std::string& filename, Model& model);
+        bool _extract_model_from_archive_miniz(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
+
+        void _handle_start_xml_element(const char* name, const char** attributes);
+        void _handle_end_xml_element(const char* name);
+
+        bool _handle_start_model(const char** attributes, unsigned int num_attributes);
+        bool _handle_end_model();
+
+        bool _handle_start_resources(const char** attributes, unsigned int num_attributes);
+        bool _handle_end_resources();
+
+        bool _handle_start_object(const char** attributes, unsigned int num_attributes);
+        bool _handle_end_object();
+
+        bool _handle_start_mesh(const char** attributes, unsigned int num_attributes);
+        bool _handle_end_mesh();
+
+        bool _handle_start_vertices(const char** attributes, unsigned int num_attributes);
+        bool _handle_end_vertices();
+
+        bool _handle_start_vertex(const char** attributes, unsigned int num_attributes);
+        bool _handle_end_vertex();
+
+        bool _handle_start_triangles(const char** attributes, unsigned int num_attributes);
+        bool _handle_end_triangles();
+
+        bool _handle_start_triangle(const char** attributes, unsigned int num_attributes);
+        bool _handle_end_triangle();
+
+        bool _handle_start_components(const char** attributes, unsigned int num_attributes);
+        bool _handle_end_components();
+
+        bool _handle_start_component(const char** attributes, unsigned int num_attributes);
+        bool _handle_end_component();
+
+        bool _handle_start_build(const char** attributes, unsigned int num_attributes);
+        bool _handle_end_build();
+
+        bool _handle_start_item(const char** attributes, unsigned int num_attributes);
+        bool _handle_end_item();
+
+        bool _create_object_instance(int object_id, const Matrix4x4& matrix, unsigned int recur_counter);
+
+        void _apply_transform(ModelObject& object, const Matrix4x4& matrix);
+        void _apply_transform(ModelInstance& instance, const Matrix4x4& matrix);
+
+        static void XMLCALL _handle_start_xml_element(void* userData, const char* name, const char** attributes);
+        static void XMLCALL _handle_end_xml_element(void* userData, const char* name);
+    };
+
+    _3MF_Importer::Component::Component(int object_id)
+        : object_id(object_id)
+        , matrix(Matrix4x4::Identity())
+    {
+    }
+
+    _3MF_Importer::Component::Component(int object_id, const Matrix4x4& matrix)
+        : object_id(object_id)
+        , matrix(matrix)
+    {
+    }
+
+    bool _3MF_Importer::CurrentObject::Geometry::empty()
+    {
+        return vertices.empty() || triangles.empty();
+    }
+
+    void _3MF_Importer::CurrentObject::Geometry::reset()
+    {
+        vertices.clear();
+        triangles.clear();
+    }
+
+    _3MF_Importer::CurrentObject::CurrentObject()
+    {
+        reset();
+    }
+
+    void _3MF_Importer::CurrentObject::reset()
+    {
+        id = -1;
+        geometry.reset();
+        object = nullptr;
+        mesh = TriangleMesh();
+        components.clear();
+    }
+
+    _3MF_Importer::Instance::Instance(ModelInstance* instance, const Matrix4x4& matrix)
+        : instance(instance)
+        , matrix(matrix)
+    {
+    }
+
+    _3MF_Importer::_3MF_Importer()
+        : m_xml_parser(nullptr)
+        , m_model(nullptr)   
+        , m_unit_factor(1.0f)
+    {
+    }
+
+    _3MF_Importer::~_3MF_Importer()
+    {
+        _destroy_xml_parser();
+    }
+
+    bool _3MF_Importer::load_model_from_file(const std::string& filename, Model& model)
+    {
+        m_model = &model;
+        m_unit_factor = 1.0f;
+        m_curr_object.reset();
+        m_objects.clear();
+        m_objects_aliases.clear();
+        m_instances.clear();
+        m_errors.clear();
+
+        return _load_model_from_file_miniz(filename, model);
+    }
+
+    const std::vector<std::string>& _3MF_Importer::get_errors() const
+    {
+        return m_errors;
+    }
+
+    void _3MF_Importer::_destroy_xml_parser()
+    {
+        if (m_xml_parser != nullptr)
+        {
+            XML_ParserFree(m_xml_parser);
+            m_xml_parser = nullptr;
+        }
+    }
+
+    void _3MF_Importer::_stop_xml_parser()
+    {
+        if (m_xml_parser != nullptr)
+            XML_StopParser(m_xml_parser, false);
+    }
+
+    bool _3MF_Importer::_load_model_from_file_miniz(const std::string& filename, Model& model)
+    {
+        mz_zip_archive archive;
+        mz_zip_zero_struct(&archive);
+       
+        mz_bool res = mz_zip_reader_init_file(&archive, filename.c_str(), 0);
+        if (res == 0)
+        {
+            m_errors.push_back("Unable to open the file");
+            return false;
+        }
+
+        mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
+
+        mz_zip_archive_file_stat stat;
+        for (mz_uint i = 0; i < num_entries; ++i)
+        {
+            if (mz_zip_reader_file_stat(&archive, i, &stat))
+            {
+                std::string name(stat.m_filename);
+                std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) -> unsigned char { return std::tolower(c); });
+                std::replace(name.begin(), name.end(), '/', '\\');
+
+                if ((name.find(MODEL_FOLDER) == 0) && (name.rfind(MODEL_EXTENSION) == name.length() - MODEL_EXTENSION.length()))
+                {
+                    if (!_extract_model_from_archive_miniz(archive, stat))
+                    {
+                        mz_zip_reader_end(&archive);
+                        m_errors.push_back("Archive does not contain a valid model");
+                        return false;
+                    }
+                }
+            }
+        }
+
+        mz_zip_reader_end(&archive);
+        return true;
+    }
+
+    bool _3MF_Importer::_extract_model_from_archive_miniz(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
+    {
+        _destroy_xml_parser();
+
+        m_xml_parser = XML_ParserCreate(nullptr);
+        if (m_xml_parser == nullptr)
+        {
+            m_errors.push_back("Unable to create parser");
+            return false;
+        }
+
+        XML_SetUserData(m_xml_parser, (void*)this);
+        XML_SetElementHandler(m_xml_parser, _3MF_Importer::_handle_start_xml_element, _3MF_Importer::_handle_end_xml_element);
+
+        void* parser_buffer = XML_GetBuffer(m_xml_parser, (int)stat.m_uncomp_size);
+        if (parser_buffer == nullptr)
+        {
+            m_errors.push_back("Unable to create buffer");
+            return false;
+        }
+
+        mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0);
+        if (res == 0)
+        {
+            m_errors.push_back("Error while reading data to buffer");
+            return false;
+        }
+
+        if (!XML_ParseBuffer(m_xml_parser, (int)stat.m_uncomp_size, 1))
+        {
+            char error_buf[1024];
+            ::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), XML_GetCurrentLineNumber(m_xml_parser));
+            m_errors.push_back(error_buf);
+            return false;
+        }
+
+        return true;
+    }
+
+    void _3MF_Importer::_handle_start_xml_element(const char* name, const char** attributes)
+    {
+        if (m_xml_parser == nullptr)
+            return;
+
+        bool res = true;
+        unsigned int num_attributes = (unsigned int)XML_GetSpecifiedAttributeCount(m_xml_parser);
+
+        if (::strcmp(MODEL_TAG, name) == 0)
+            res = _handle_start_model(attributes, num_attributes);
+        else if (::strcmp(RESOURCES_TAG, name) == 0)
+            res = _handle_start_resources(attributes, num_attributes);
+        else if (::strcmp(OBJECT_TAG, name) == 0)
+            res = _handle_start_object(attributes, num_attributes);
+        else if (::strcmp(MESH_TAG, name) == 0)
+            res = _handle_start_mesh(attributes, num_attributes);
+        else if (::strcmp(VERTICES_TAG, name) == 0)
+            res = _handle_start_vertices(attributes, num_attributes);
+        else if (::strcmp(VERTEX_TAG, name) == 0)
+            res = _handle_start_vertex(attributes, num_attributes);
+        else if (::strcmp(TRIANGLES_TAG, name) == 0)
+            res = _handle_start_triangles(attributes, num_attributes);
+        else if (::strcmp(TRIANGLE_TAG, name) == 0)
+            res = _handle_start_triangle(attributes, num_attributes);
+        else if (::strcmp(COMPONENTS_TAG, name) == 0)
+            res = _handle_start_components(attributes, num_attributes);
+        else if (::strcmp(COMPONENT_TAG, name) == 0)
+            res = _handle_start_component(attributes, num_attributes);
+        else if (::strcmp(BUILD_TAG, name) == 0)
+            res = _handle_start_build(attributes, num_attributes);
+        else if (::strcmp(ITEM_TAG, name) == 0)
+            res = _handle_start_item(attributes, num_attributes);
+
+        if (!res)
+            _stop_xml_parser();
+    }
+
+    void _3MF_Importer::_handle_end_xml_element(const char* name)
+    {
+        if (m_xml_parser == nullptr)
+            return;
+
+        bool res = true;
+
+        if (::strcmp(MODEL_TAG, name) == 0)
+            res = _handle_end_model();
+        else if (::strcmp(RESOURCES_TAG, name) == 0)
+            res = _handle_end_resources();
+        else if (::strcmp(OBJECT_TAG, name) == 0)
+            res = _handle_end_object();
+        else if (::strcmp(MESH_TAG, name) == 0)
+            res = _handle_end_mesh();
+        else if (::strcmp(VERTICES_TAG, name) == 0)
+            res = _handle_end_vertices();
+        else if (::strcmp(VERTEX_TAG, name) == 0)
+            res = _handle_end_vertex();
+        else if (::strcmp(TRIANGLES_TAG, name) == 0)
+            res = _handle_end_triangles();
+        else if (::strcmp(TRIANGLE_TAG, name) == 0)
+            res = _handle_end_triangle();
+        else if (::strcmp(COMPONENTS_TAG, name) == 0)
+            res = _handle_end_components();
+        else if (::strcmp(COMPONENT_TAG, name) == 0)
+            res = _handle_end_component();
+        else if (::strcmp(BUILD_TAG, name) == 0)
+            res = _handle_end_build();
+        else if (::strcmp(ITEM_TAG, name) == 0)
+            res = _handle_end_item();
+
+        if (!res)
+            _stop_xml_parser();
+    }
+
+    bool _3MF_Importer::_handle_start_model(const char** attributes, unsigned int num_attributes)
+    {
+        m_unit_factor = get_unit_factor(get_attribute_value_string(attributes, num_attributes, UNIT_ATTR));
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_end_model()
+    {
+        // deletes all non-built or non-instanced objects
+        for (const IdToModelObjectMap::value_type& object : m_objects)
+        {
+            if ((object.second != nullptr) && (object.second->instances.size() == 0))
+                m_model->delete_object(object.second);
+        }
+
+        // applies instances' matrices
+        for (Instance& instance : m_instances)
+        {
+            if (instance.instance != nullptr)
+            {
+                ModelObject* object = instance.instance->get_object();
+                if (object != nullptr)
+                {
+                    if (object->instances.size() == 1)
+                    {
+                        // single instance -> apply the matrix to object geometry
+                        _apply_transform(*object, instance.matrix);
+                    }
+                    else
+                    {
+                        // multiple instances -> apply the matrix to the instance
+                        _apply_transform(*instance.instance, instance.matrix);
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_start_resources(const char** attributes, unsigned int num_attributes)
+    {
+        // do nothing
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_end_resources()
+    {
+        // do nothing
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_start_object(const char** attributes, unsigned int num_attributes)
+    {
+        // reset current data
+        m_curr_object.reset();
+
+        if (is_valid_object_type(get_attribute_value_string(attributes, num_attributes, TYPE_ATTR)))
+        {
+            // create new object (it may be removed later if no instances are generated from it)
+            m_curr_object.object = m_model->add_object();
+            if (m_curr_object.object == nullptr)
+            {
+                m_errors.push_back("Unable to create object");
+                return false;
+            }
+
+            // set object data
+            m_curr_object.object->name = get_attribute_value_string(attributes, num_attributes, NAME_ATTR);
+            m_curr_object.id = get_attribute_value_int(attributes, num_attributes, ID_ATTR);
+        }
+
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_end_object()
+    {
+        if (m_curr_object.object != nullptr)
+        {
+            if (m_curr_object.geometry.empty())
+            {
+                // no geometry defined
+                // remove the object from the model
+                m_model->delete_object(m_curr_object.object);
+
+                if (m_curr_object.components.empty())
+                {
+                    // no components defined -> invalid object, delete it
+                    IdToModelObjectMap::iterator object_item = m_objects.find(m_curr_object.id);
+                    if (object_item != m_objects.end())
+                        m_objects.erase(object_item);
+
+                    IdToAliasesMap::iterator alias_item = m_objects_aliases.find(m_curr_object.id);
+                    if (alias_item != m_objects_aliases.end())
+                        m_objects_aliases.erase(alias_item);
+                }
+                else
+                    // adds components to aliases
+                    m_objects_aliases.insert(IdToAliasesMap::value_type(m_curr_object.id, m_curr_object.components));
+            }
+            else
+            {
+                // geometry defined, add it to the object
+
+                ModelVolume* volume = m_curr_object.object->add_volume(m_curr_object.mesh);
+                if (volume == nullptr)
+                {
+                    m_errors.push_back("Unable to add volume");
+                    return false;
+                }
+
+                stl_file& stl = volume->mesh.stl;
+                stl.stats.type = inmemory;
+                stl.stats.number_of_facets = (uint32_t)m_curr_object.geometry.triangles.size() / 3;
+                stl.stats.original_num_facets = (int)stl.stats.number_of_facets;
+                stl_allocate(&stl);
+                for (size_t i = 0; i < m_curr_object.geometry.triangles.size(); /*nothing*/)
+                {
+                    stl_facet& facet = stl.facet_start[i / 3];
+                    for (unsigned int v = 0; v < 3; ++v)
+                    {
+                        ::memcpy((void*)&facet.vertex[v].x, (const void*)&m_curr_object.geometry.vertices[m_curr_object.geometry.triangles[i++] * 3], 3 * sizeof(float));
+                    }
+                }
+                stl_get_size(&stl);
+                volume->mesh.repair();
+
+                // stores the object for later use
+                if (m_objects.find(m_curr_object.id) == m_objects.end())
+                {
+                    m_objects.insert(IdToModelObjectMap::value_type(m_curr_object.id, m_curr_object.object));
+                    m_objects_aliases.insert(IdToAliasesMap::value_type(m_curr_object.id, ComponentsList(1, Component(m_curr_object.id)))); // aliases itself
+                }
+                else
+                {
+                    m_errors.push_back("Found object with duplicate id");
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_start_mesh(const char** attributes, unsigned int num_attributes)
+    {
+        // reset current geometry
+        m_curr_object.geometry.reset();
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_end_mesh()
+    {
+        // do nothing
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_start_vertices(const char** attributes, unsigned int num_attributes)
+    {
+        // reset current vertices
+        m_curr_object.geometry.vertices.clear();
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_end_vertices()
+    {
+        // do nothing
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_start_vertex(const char** attributes, unsigned int num_attributes)
+    {
+        // appends the vertex coordinates
+        // missing values are set equal to ZERO
+        m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, X_ATTR));
+        m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, Y_ATTR));
+        m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, Z_ATTR));
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_end_vertex()
+    {
+        // do nothing
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_start_triangles(const char** attributes, unsigned int num_attributes)
+    {
+        // reset current triangles
+        m_curr_object.geometry.triangles.clear();
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_end_triangles()
+    {
+        // do nothing
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_start_triangle(const char** attributes, unsigned int num_attributes)
+    {
+        // we are ignoring the following attributes:
+        // p1
+        // p2
+        // p3
+        // pid
+        // see specifications
+
+        // appends the triangle's vertices indices
+        // missing values are set equal to ZERO
+        m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V1_ATTR));
+        m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V2_ATTR));
+        m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V3_ATTR));
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_end_triangle()
+    {
+        // do nothing
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_start_components(const char** attributes, unsigned int num_attributes)
+    {
+        // reset current components
+        m_curr_object.components.clear();
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_end_components()
+    {
+        // do nothing
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_start_component(const char** attributes, unsigned int num_attributes)
+    {
+        int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR);
+        Matrix4x4 matrix = get_matrix_from_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
+
+        IdToModelObjectMap::iterator object_item = m_objects.find(object_id);
+        if (object_item == m_objects.end())
+        {
+            IdToAliasesMap::iterator alias_item = m_objects_aliases.find(object_id);
+            if (alias_item == m_objects_aliases.end())
+            {
+                m_errors.push_back("Found component with invalid object id");
+                return false;
+            }
+        }
+
+        m_curr_object.components.emplace_back(object_id, matrix);
+
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_end_component()
+    {
+        // do nothing
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_start_build(const char** attributes, unsigned int num_attributes)
+    {
+        // do nothing
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_end_build()
+    {
+        // do nothing
+        return true;
+    }
+
+    bool _3MF_Importer::_handle_start_item(const char** attributes, unsigned int num_attributes)
+    {
+        // we are ignoring the following attributes
+        // thumbnail
+        // partnumber
+        // pid
+        // pindex
+        // see specifications
+
+        int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR);
+        Matrix4x4 matrix = get_matrix_from_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
+
+        return _create_object_instance(object_id, matrix, 1);
+    }
+
+    bool _3MF_Importer::_handle_end_item()
+    {
+        // do nothing
+        return true;
+    }
+
+    bool _3MF_Importer::_create_object_instance(int object_id, const Matrix4x4& matrix, unsigned int recur_counter)
+    {
+        static const unsigned int MAX_RECURSIONS = 10;
+
+        // escape from circular aliasing
+        if (recur_counter > MAX_RECURSIONS)
+        {
+            m_errors.push_back("Too many recursions");
+            return false;
+        }
+
+        IdToAliasesMap::iterator it = m_objects_aliases.find(object_id);
+        if (it == m_objects_aliases.end())
+        {
+            m_errors.push_back("Found item with invalid object id");
+            return false;
+        }
+
+        if ((it->second.size() == 1) && (it->second[0].object_id == object_id))
+        {
+            // aliasing to itself
+
+            IdToModelObjectMap::iterator object_item = m_objects.find(object_id);
+            if ((object_item == m_objects.end()) || (object_item->second == nullptr))
+            {
+                m_errors.push_back("Found invalid object");
+                return false;
+            }
+            else
+            {
+                ModelInstance* instance = object_item->second->add_instance();
+                if (instance == nullptr)
+                {
+                    m_errors.push_back("Unable to add object instance");
+                    return false;
+                }
+
+                m_instances.emplace_back(instance, matrix);
+            }
+        }
+        else
+        {
+            // recursively process nested components
+            for (const Component& component : it->second)
+            {
+                if (!_create_object_instance(component.object_id, matrix * component.matrix, recur_counter + 1))
+                    return false;
+            }
+        }
+
+        return true;
+    }
+
+    void _3MF_Importer::_apply_transform(ModelObject& object, const Matrix4x4& matrix)
+    {
+        float matrix3x4[12] = { matrix(0, 0), matrix(0, 1), matrix(0, 2), matrix(0, 3),
+                                matrix(1, 0), matrix(1, 1), matrix(1, 2), matrix(1, 3),
+                                matrix(2, 0), matrix(2, 1), matrix(2, 2), matrix(2, 3) };
+
+        object.transform(matrix3x4);
+    }
+
+    void _3MF_Importer::_apply_transform(ModelInstance& instance, const Matrix4x4& matrix)
+    {
+        // slic3r ModelInstance cannot be transformed using a matrix
+        // we extract from the given matrix only the values currently used
+
+        // translation
+        double offset_x = (double)matrix(0, 3);
+        double offset_y = (double)matrix(1, 3);
+        double offset_z = (double)matrix(2, 3);
+
+        // scale
+        double sx = ::sqrt(sqr((double)matrix(0, 0)) + sqr((double)matrix(1, 0)) + sqr((double)matrix(2, 0)));
+        double sy = ::sqrt(sqr((double)matrix(0, 1)) + sqr((double)matrix(1, 1)) + sqr((double)matrix(2, 1)));
+        double sz = ::sqrt(sqr((double)matrix(0, 2)) + sqr((double)matrix(1, 2)) + sqr((double)matrix(2, 2)));
+
+        // invalid scale value, return
+        if ((sx == 0.0) || (sy == 0.0) || (sz == 0.0))
+            return;
+
+        // non-uniform scale value, return
+        if ((std::abs(sx - sy) > 0.00001) || (std::abs(sx - sz) > 0.00001))
+            return;
+
+        // rotations (extracted using quaternion)
+        double inv_sx = 1.0 / sx;
+        double inv_sy = 1.0 / sy;
+        double inv_sz = 1.0 / sz;
+        
+        Eigen::Matrix<double, 3, 3, Eigen::RowMajor> m3x3;
+        m3x3 << (double)matrix(0, 0) * inv_sx, (double)matrix(0, 1) * inv_sy, (double)matrix(0, 2) * inv_sz,
+                (double)matrix(1, 0) * inv_sx, (double)matrix(1, 1) * inv_sy, (double)matrix(1, 2) * inv_sz,
+                (double)matrix(2, 0) * inv_sx, (double)matrix(2, 1) * inv_sy, (double)matrix(2, 2) * inv_sz;
+
+        double qw = 0.5 * ::sqrt(std::max(0.0, 1.0 + m3x3(0, 0) + m3x3(1, 1) + m3x3(2, 2)));
+        double qx = 0.5 * ::sqrt(std::max(0.0, 1.0 + m3x3(0, 0) - m3x3(1, 1) - m3x3(2, 2)));
+        double qy = 0.5 * ::sqrt(std::max(0.0, 1.0 - m3x3(0, 0) + m3x3(1, 1) - m3x3(2, 2)));
+        double qz = 0.5 * ::sqrt(std::max(0.0, 1.0 - m3x3(0, 0) - m3x3(1, 1) + m3x3(2, 2)));
+
+        double q_magnitude = ::sqrt(sqr(qw) + sqr(qx) + sqr(qy) + sqr(qz));
+
+        // invalid length, return
+        if (q_magnitude == 0.0)
+            return;
+
+        double inv_q_magnitude = 1.0 / q_magnitude;
+
+        qw *= inv_q_magnitude;
+        qx *= inv_q_magnitude;
+        qy *= inv_q_magnitude;
+        qz *= inv_q_magnitude;
+
+        double test = qx * qy + qz * qw;
+        double angle_x, angle_y, angle_z;
+
+        if (test > 0.499)
+        {
+            // singularity at north pole
+            angle_x = 0.0;
+            angle_y = 2.0 * ::atan2(qx, qw);
+            angle_z = 0.5 * PI;
+        }
+        else if (test < -0.499)
+        {
+            // singularity at south pole
+            angle_x = 0.0;
+            angle_y = -2.0 * ::atan2(qx, qw);
+            angle_z = -0.5 * PI;
+        }
+        else
+        {
+            angle_x = ::atan2(2.0 * qx * qw - 2.0 * qy * qz, 1.0 - 2.0 * sqr(qx) - 2.0 * sqr(qz));
+            angle_y = ::atan2(2.0 * qy * qw - 2.0 * qx * qz, 1.0 - 2.0 * sqr(qy) - 2.0 * sqr(qz));
+            angle_z = ::asin(2.0 * qx * qy + 2.0 * qz * qw);
+
+            if (angle_x < 0.0)
+                angle_x += 2.0 * PI;
+
+            if (angle_y < 0.0)
+                angle_y += 2.0 * PI;
+
+            if (angle_z < 0.0)
+                angle_z += 2.0 * PI;
+        }
+
+        instance.offset.x = offset_x;
+        instance.offset.y = offset_y;
+        instance.scaling_factor = sx;
+        instance.rotation = angle_z;
+    }
+
+    void XMLCALL _3MF_Importer::_handle_start_xml_element(void* userData, const char* name, const char** attributes)
+    {
+        _3MF_Importer* importer = (_3MF_Importer*)userData;
+        if (importer != nullptr)
+            importer->_handle_start_xml_element(name, attributes);
+    }
+
+    void XMLCALL _3MF_Importer::_handle_end_xml_element(void* userData, const char* name)
+    {
+        _3MF_Importer* importer = (_3MF_Importer*)userData;
+        if (importer != nullptr)
+            importer->_handle_end_xml_element(name);
+    }
+
+    bool load_3mf(const char* path, Model* model, const char* object_name)
+    {
+        if ((path == nullptr) || (model == nullptr))
+            return false;
+
+        _3MF_Importer importer;
+        bool res = importer.load_model_from_file(path, *model);
+
+        if (!res)
+        {
+            const std::vector<std::string>& errors = importer.get_errors();
+            int a = 0;
+        }
+
+        return res;
+    }
+} // namespace Slic3r
diff --git a/xs/src/libslic3r/Format/3mf.hpp b/xs/src/libslic3r/Format/3mf.hpp
new file mode 100644
index 000000000..11eb4c388
--- /dev/null
+++ b/xs/src/libslic3r/Format/3mf.hpp
@@ -0,0 +1,12 @@
+#ifndef slic3r_Format_3mf_hpp_
+#define slic3r_Format_3mf_hpp_
+
+namespace Slic3r {
+
+    class Model;
+
+    // Load an 3mf file into a provided model.
+    extern bool load_3mf(const char* path, Model* model, const char* object_name = nullptr);
+}; // namespace Slic3r
+
+#endif /* slic3r_Format_3mf_hpp_ */
diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index 88a0b8279..b86b7e55e 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -5,6 +5,7 @@
 #include "Format/OBJ.hpp"
 #include "Format/PRUS.hpp"
 #include "Format/STL.hpp"
+#include "Format/3mf.hpp"
 
 #include <float.h>
 
@@ -53,9 +54,11 @@ Model Model::read_from_file(const std::string &input_file, bool add_default_inst
     else if (boost::algorithm::iends_with(input_file, ".prusa"))
         result = load_prus(input_file.c_str(), &model);
 #endif /* SLIC3R_PRUS */
+    else if (boost::algorithm::iends_with(input_file, ".3mf"))
+        result = load_3mf(input_file.c_str(), &model);
     else
-        throw std::runtime_error("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension.");
-    
+        throw std::runtime_error("Unknown file format. Input file must have .stl, .obj, .amf(.xml), .3mf or .prusa extension.");
+
     if (! result)
         throw std::runtime_error("Loading of a model file failed.");
 
@@ -115,6 +118,23 @@ void Model::delete_object(size_t idx)
     this->objects.erase(i);
 }
 
+void Model::delete_object(ModelObject* object)
+{
+    if (object == nullptr)
+        return;
+
+    for (ModelObjectPtrs::iterator it = objects.begin(); it != objects.end(); ++it)
+    {
+        ModelObject* obj = *it;
+        if (obj == object)
+        {
+            delete obj;
+            objects.erase(it);
+            return;
+        }
+    }
+}
+
 void Model::clear_objects()
 {
     for (ModelObject *o : this->objects)
@@ -607,6 +627,20 @@ void ModelObject::rotate(float angle, const Axis &axis)
     this->invalidate_bounding_box();
 }
 
+void ModelObject::transform(const float* matrix3x4)
+{
+    if (matrix3x4 == nullptr)
+        return;
+
+    for (ModelVolume* v : volumes)
+    {
+        v->mesh.transform(matrix3x4);
+    }
+
+    origin_translation = Pointf3(0.0f, 0.0f, 0.0f);
+    invalidate_bounding_box();
+}
+
 void ModelObject::mirror(const Axis &axis)
 {
     for (ModelVolume *v : this->volumes)
diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp
index 636dfa1f4..317d7ccbb 100644
--- a/xs/src/libslic3r/Model.hpp
+++ b/xs/src/libslic3r/Model.hpp
@@ -119,6 +119,7 @@ public:
     void translate(coordf_t x, coordf_t y, coordf_t z);
     void scale(const Pointf3 &versor);
     void rotate(float angle, const Axis &axis);
+    void transform(const float* matrix3x4);
     void mirror(const Axis &axis);
     size_t materials_count() const;
     size_t facets_count() const;
@@ -244,6 +245,7 @@ public:
     ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh);
     ModelObject* add_object(const ModelObject &other, bool copy_volumes = true);
     void delete_object(size_t idx);
+    void delete_object(ModelObject* object);
     void clear_objects();
     
     ModelMaterial* add_material(t_model_material_id material_id);
diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp
index 7afa4871c..c2c64e427 100644
--- a/xs/src/libslic3r/TriangleMesh.cpp
+++ b/xs/src/libslic3r/TriangleMesh.cpp
@@ -375,6 +375,15 @@ void TriangleMesh::mirror_z()
     this->mirror(Z);
 }
 
+void TriangleMesh::transform(const float* matrix3x4)
+{
+    if (matrix3x4 == nullptr)
+        return;
+
+    stl_transform(&stl, const_cast<float*>(matrix3x4));
+    stl_invalidate_shared_vertices(&stl);
+}
+
 void TriangleMesh::align_to_origin()
 {
     this->translate(
diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp
index b28f3a310..3192c90b6 100644
--- a/xs/src/libslic3r/TriangleMesh.hpp
+++ b/xs/src/libslic3r/TriangleMesh.hpp
@@ -47,6 +47,7 @@ public:
     void mirror_x();
     void mirror_y();
     void mirror_z();
+    void transform(const float* matrix3x4);
     void align_to_origin();
     void rotate(double angle, Point* center);
     TriangleMeshPtrs split() const;