diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx
index 8a34a9a11..ee3a57fe8 100644
--- a/resources/profiles/PrusaResearch.idx
+++ b/resources/profiles/PrusaResearch.idx
@@ -1,181 +1,184 @@
-min_slic3r_version = 2.4.0-alpha0
-1.4.0-alpha7 Updated brim_separation value. Updated Prusa MINI end g-code. Added Filamentworld filament profiles. 
-1.4.0-alpha6 Added nozzle priming after M600. Added nozzle diameter checks for 0.8 nozzle printer profiles. Updated FW version. Increased number of top solid infill layers (0.2 layer height).
-1.4.0-alpha5 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S).
-1.4.0-alpha4 Decreased Area Fill (SL1S).
-1.4.0-alpha3 Updated SL1S tilt times.
-1.4.0-alpha2 Updated Prusa MINI machine limits.
-1.4.0-alpha1 Added new SL1S resin profiles.
-1.4.0-alpha0 Bumped up config version.
-1.3.0-alpha2 Added SL1S SPEED profiles.
-1.3.0-alpha1 Added Prusament PCCF. Increased travel acceleration for Prusa MINI. Updated start g-code for Prusa MINI. Added multiple add:north and Extrudr filament profiles. Updated Z travel speed values.
-1.3.0-alpha0 Disabled thick bridges, updated support settings.
-min_slic3r_version = 2.3.2-alpha0
-1.3.1 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S).
-1.3.0 Added SL1S SPEED profiles.
-min_slic3r_version = 2.3.0-rc1
-1.2.8 Added multiple add:north and Extrudr filament profiles.
-1.2.7 Updated "Prusament PC Blend Carbon Fiber" profile for Prusa MINI.
-1.2.6 Added filament profile for "Prusament PC Blend Carbon Fiber".
-1.2.5 Updated firmware version. Added filament profiles. Various improvements.
-1.2.4 Updated cost/density values in filament settings. Various changes in print settings.
-1.2.3 Updated firmware version. Updated end g-code in MMU2 printer profiles.
-1.2.2 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. 
-1.2.1 Updated FW version for MK2.5 family printers.
-1.2.0 Added full_fan_speed_layer value for PETG. Increased support interface spacing for 0.6mm nozzle profiles. Updated firmware version.
-min_slic3r_version = 2.3.0-beta2
-1.2.0-beta1 Updated end g-code. Added full_fan_speed_layer values.
-min_slic3r_version = 2.3.0-beta0
-1.2.0-beta0 Adjusted infill anchor limits. Added filament spool weights. 
-min_slic3r_version = 2.3.0-alpha4
-1.2.0-alpha1 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles.
-1.2.0-alpha0 Added filament spool weights
-min_slic3r_version = 2.2.0-alpha3
-1.1.14 Updated firmware version.
-1.1.13 Updated firmware version. Updated end g-code in MMU2 printer profiles.
-1.1.12 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. 
-1.1.11 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles.
-1.1.10 Updated firmware version.
-1.1.9 Updated K values in filament profiles (linear advance). Added new filament profiles and SLA materials.
-1.1.8 Updated start/end g-code scripts for MK3 family printer profiles (reduced extruder motor current for some print profiles). Added new filament and SLA material profiles.
-1.1.7 Updated end g-code for MMU2 Single printer profiles. Added/updated filament and SLA material profiles.
-1.1.6 Updated firmware version for MK2.5/S and MK3/S.
-1.1.5 Updated MMU1 specific retraction settings for Prusament PC Blend
-1.1.4 Added Prusament PC Blend filament profile.
-1.1.3 Added SLA material and filament profile
-1.1.2 Added renamed_from fields for PETG filaments to indicate that they were renamed from PET.
-1.1.1 Added Verbatim and Fiberlogy PETG filament profiles. Updated auto cooling settings for ABS.
-1.1.1-beta Updated for PrusaSlicer 2.2.0-beta
-1.1.1-alpha4 Extended list of default filaments to be installed, top/bottom_solid_min_thickness defined, infill_acceleration changed etc
-1.1.1-alpha3 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer.
-# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer,
-# so they will see the print bed.
-max_slic3r_version = 2.2.0-alpha2
-min_slic3r_version = 2.2.0-alpha0
-1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles.
-1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0
-min_slic3r_version = 2.1.1-beta0
-1.0.12 Updated firmware version.
-1.0.11 Updated firmware version.
-1.0.10 Updated firmware version for MK2.5/S and MK3/S.
-1.0.9 Updated firmware version for MK2.5/S and MK3/S.
-1.0.8 Various changes in FFF profiles, new filaments/materials added. See changelog.
-1.0.7 Updated layer height limits for MINI
-1.0.6 Added Prusa MINI profiles
-min_slic3r_version = 2.1.0-alpha0
-1.0.5 Added SLA materials
-1.0.4 Updated firmware version and 0.25mm nozzle profiles
-1.0.3 Added filament profiles
-1.0.2 Added SLA materials
-1.0.1 Updated MK3 firmware version check to 3.8.0, new soluble support profiles for 0.6mm nozzle diameter MMU2S printers.
-1.0.0 Updated end G-code for the MMU2 profiles to lift the extruder at the end of print. Wipe tower bridging distance was made smaller for soluble supports.
-1.0.0-beta1 Updated color for the ASA filaments to differ from the other filaments. Single extruder printers now have no extruder color assigned, obects and toolpaths will be colored with the color of the active filament.
-1.0.0-beta0 Printer model checks in start G-codes, ASA filament profiles, limits on min / max SL1 exposition times
-1.0.0-alpha2 Printer model and nozzle diameter check
-1.0.0-alpha1 Added Prusament ASA profile
-1.0.0-alpha0 Filament specific retract for PET and similar copolymers, and for FLEX
-min_slic3r_version = 1.42.0-alpha6
-0.8.11 Updated firmware version.
-0.8.10 Updated firmware version.
-0.8.9 Updated firmware version for MK2.5/S and MK3/S.
-0.8.8 Updated firmware version for MK2.5/S and MK3/S.
-0.8.7 Updated firmware version
-0.8.6 Updated firmware version for MK2.5/S and MK3/S
-0.8.5 Updated SL1 printer and material settings
-0.8.4 Added Prusament ASA profile
-0.8.3 FW version and SL1 materials update
-0.8.2 FFF and SL1 settings update
-0.8.1 Output settings and SLA materials update
-0.8.0 Updated for the PrusaSlicer 2.0.0 final release
-0.8.0-rc2 Updated firmware versions for MK2.5/S and MK3/S
-0.8.0-rc1 Updated SLA profiles
-0.8.0-rc Updated for the PrusaSlicer 2.0.0-rc release
-0.8.0-beta4 Updated SLA profiles
-0.8.0-beta3 Updated SLA profiles
-0.8.0-beta2 Updated SLA profiles
-0.8.0-beta1 Updated SLA profiles
-0.8.0-beta Updated SLA profiles
-0.8.0-alpha9 Updated SLA and FFF profiles
-0.8.0-alpha8 Updated SLA profiles
-0.8.0-alpha7 Updated SLA profiles
-0.8.0-alpha6 Updated SLA profiles
-min_slic3r_version = 1.42.0-alpha
-0.8.0-alpha Updated SLA profiles
-0.4.0-alpha4 Updated SLA profiles
-0.4.0-alpha3 Update of SLA profiles
-0.4.0-alpha2 First SLA profiles
-min_slic3r_version = 1.41.3-alpha
-0.4.12 Updated firmware version for MK2.5/S and MK3/S.
-0.4.11 Updated firmware version for MK2.5/S and MK3/S.
-0.4.10 Updated firmware version
-0.4.9 Updated firmware version for MK2.5/S and MK3/S
-0.4.8 MK2.5/3/S FW update
-0.4.7 MK2/S/MMU FW update
-0.4.6 Updated firmware versions for MK2.5/S and MK3/S
-0.4.5 Enabled remaining time support for MK2/S/MMU1
-0.4.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
-0.4.3 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
-0.4.2 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
-0.4.1 New MK2.5S and MK3S FW versions
-0.4.0 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt 
-min_slic3r_version = 1.41.1
-0.3.11 Updated firmware version for MK2.5/S and MK3/S.
-0.3.10 Updated firmware version
-0.3.9 Updated firmware version for MK2.5/S and MK3/S
-0.3.8 MK2.5/3/S FW update
-0.3.7 MK2/S/MMU FW update
-0.3.6 Updated firmware versions for MK2.5 and MK3
-0.3.5 New MK2.5 and MK3 FW versions
-0.3.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt 
-0.3.3 Prusament PETG released
-0.3.2 New MK2.5 and MK3 FW versions
-0.3.1 New MK2.5 and MK3 FW versions
-0.3.0 New MK2.5 and MK3 FW version
-min_slic3r_version = 1.41.0-alpha
-0.2.9 New MK2.5 and MK3 FW versions
-0.2.8 New MK2.5 and MK3 FW version
-min_slic3r_version = 1.41.1
-0.2.7 New MK2.5 and MK3 FW version
-0.2.6 Added MMU2 MK2.5 settings
-min_slic3r_version = 1.41.0-alpha
-0.2.5 Prusament is out - added prusament settings
-0.2.4 Added soluble support profiles for MMU2
-0.2.3 Added materials for MMU2 single mode, edited MK3 xy stealth feedrate limit
-0.2.2 Edited MMU2 Single mode purge line
-0.2.1 Added PET and BVOH settings for MMU2
-0.2.0-beta5 Fixed MMU1 ramming parameters
-0.2.0-beta4 Added filament loading speed at start, increased minimal purge on wipe tower
-0.2.0-beta3 Edited ramming parameters and filament cooling moves for MMU2
-0.2.0-beta2 Edited first layer speed and wipe tower position
-0.2.0-beta Removed limit on the MK3MMU2 height, added legacy M204 S T format to the MK2 profiles
-0.2.0-alpha8 Added filament_load/unload_time for the PLA/ABS MMU2 filament presets.
-0.2.0-alpha7 Vojtech's fix the incorrect *MK3* references
-0.2.0-alpha6 Jindra's way to fix the 0.2.0-alpha5 version
-0.2.0-alpha5 Bumped up firmware versions for MK2.5/MK3 to 3.3.1, disabled priming areas for MK3MMU2
-0.2.0-alpha4 Extended the custom start/end G-codes of the MMU2.0 printers for no priming towers.
-0.2.0-alpha3 Adjusted machine limits for time estimates, added filament density and cost
-0.2.0-alpha2 Renamed the key MK3SMMU to MK3MMU2, added a generic PLA MMU2 material
-0.2.0-alpha1 added initial profiles for the i3 MK3 Multi Material Upgrade 2.0
-0.2.0-alpha moved machine limits from the start G-code to the new print profile parameters
-min_slic3r_version = 1.40.0
-0.1.18 Updated firmware version
-0.1.17 Updated firmware version for MK2.5/S and MK3/S
-0.1.16 MK2.5/3/S FW update
-0.1.15 MK2/S/MMU FW update
-0.1.14 Updated firmware versions for MK2.5 and MK3
-0.1.13 New MK2.5 and MK3 FW versions
-0.1.12 New MK2.5 and MK3 FW versions
-0.1.11 fw version changed to 3.3.1
-0.1.10 MK3 jerk and acceleration update
-0.1.9 edited support extrusion width for 0.25 and 0.6 nozzles
-0.1.8 extrusion width for 0,25, 0.6 and variable layer height fixes
-0.1.7 Fixed errors in 0.25mm and 0.6mm profiles
-0.1.6 Split the MK2.5 profile from the MK2S
-min_slic3r_version = 1.40.0-beta
-0.1.5 fixed printer_variant fields for the i3 MK3 0.25 and 0.6mm nozzles
-0.1.4 edited fw version, added z-raise after print
-min_slic3r_version = 1.40.0-alpha
-0.1.3 Fixed an incorrect position of the max_print_height parameter
-0.1.2 Wipe tower changes
-0.1.1 Minor print speed adjustments
-0.1.0 Initial
+min_slic3r_version = 2.4.0-alpha0
+1.4.0-alpha8 Added material profiles for Prusament Resin. Detect bridging perimeters enabled by default. 
+1.4.0-alpha7 Updated brim_separation value. Updated Prusa MINI end g-code. Added Filamentworld filament profiles. 
+1.4.0-alpha6 Added nozzle priming after M600. Added nozzle diameter checks for 0.8 nozzle printer profiles. Updated FW version. Increased number of top solid infill layers (0.2 layer height).
+1.4.0-alpha5 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S).
+1.4.0-alpha4 Decreased Area Fill (SL1S).
+1.4.0-alpha3 Updated SL1S tilt times.
+1.4.0-alpha2 Updated Prusa MINI machine limits.
+1.4.0-alpha1 Added new SL1S resin profiles.
+1.4.0-alpha0 Bumped up config version.
+1.3.0-alpha2 Added SL1S SPEED profiles.
+1.3.0-alpha1 Added Prusament PCCF. Increased travel acceleration for Prusa MINI. Updated start g-code for Prusa MINI. Added multiple add:north and Extrudr filament profiles. Updated Z travel speed values.
+1.3.0-alpha0 Disabled thick bridges, updated support settings.
+min_slic3r_version = 2.3.2-alpha0
+1.3.2 Added material profiles for Prusament Resin.
+1.3.1 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S).
+1.3.0 Added SL1S SPEED profiles.
+min_slic3r_version = 2.3.0-rc1
+1.2.9 Added material profiles for Prusament Resin.
+1.2.8 Added multiple add:north and Extrudr filament profiles.
+1.2.7 Updated "Prusament PC Blend Carbon Fiber" profile for Prusa MINI.
+1.2.6 Added filament profile for "Prusament PC Blend Carbon Fiber".
+1.2.5 Updated firmware version. Added filament profiles. Various improvements.
+1.2.4 Updated cost/density values in filament settings. Various changes in print settings.
+1.2.3 Updated firmware version. Updated end g-code in MMU2 printer profiles.
+1.2.2 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. 
+1.2.1 Updated FW version for MK2.5 family printers.
+1.2.0 Added full_fan_speed_layer value for PETG. Increased support interface spacing for 0.6mm nozzle profiles. Updated firmware version.
+min_slic3r_version = 2.3.0-beta2
+1.2.0-beta1 Updated end g-code. Added full_fan_speed_layer values.
+min_slic3r_version = 2.3.0-beta0
+1.2.0-beta0 Adjusted infill anchor limits. Added filament spool weights. 
+min_slic3r_version = 2.3.0-alpha4
+1.2.0-alpha1 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles.
+1.2.0-alpha0 Added filament spool weights
+min_slic3r_version = 2.2.0-alpha3
+1.1.14 Updated firmware version.
+1.1.13 Updated firmware version. Updated end g-code in MMU2 printer profiles.
+1.1.12 Added Prusament PVB filament profile. Added 0.8mm nozzle profiles. 
+1.1.11 Renamed MK3S and MINI printer profiles. Updated end g-code (MINI). Added new SLA materials and filament profiles.
+1.1.10 Updated firmware version.
+1.1.9 Updated K values in filament profiles (linear advance). Added new filament profiles and SLA materials.
+1.1.8 Updated start/end g-code scripts for MK3 family printer profiles (reduced extruder motor current for some print profiles). Added new filament and SLA material profiles.
+1.1.7 Updated end g-code for MMU2 Single printer profiles. Added/updated filament and SLA material profiles.
+1.1.6 Updated firmware version for MK2.5/S and MK3/S.
+1.1.5 Updated MMU1 specific retraction settings for Prusament PC Blend
+1.1.4 Added Prusament PC Blend filament profile.
+1.1.3 Added SLA material and filament profile
+1.1.2 Added renamed_from fields for PETG filaments to indicate that they were renamed from PET.
+1.1.1 Added Verbatim and Fiberlogy PETG filament profiles. Updated auto cooling settings for ABS.
+1.1.1-beta Updated for PrusaSlicer 2.2.0-beta
+1.1.1-alpha4 Extended list of default filaments to be installed, top/bottom_solid_min_thickness defined, infill_acceleration changed etc
+1.1.1-alpha3 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer.
+# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer,
+# so they will see the print bed.
+max_slic3r_version = 2.2.0-alpha2
+min_slic3r_version = 2.2.0-alpha0
+1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles.
+1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0
+min_slic3r_version = 2.1.1-beta0
+1.0.12 Updated firmware version.
+1.0.11 Updated firmware version.
+1.0.10 Updated firmware version for MK2.5/S and MK3/S.
+1.0.9 Updated firmware version for MK2.5/S and MK3/S.
+1.0.8 Various changes in FFF profiles, new filaments/materials added. See changelog.
+1.0.7 Updated layer height limits for MINI
+1.0.6 Added Prusa MINI profiles
+min_slic3r_version = 2.1.0-alpha0
+1.0.5 Added SLA materials
+1.0.4 Updated firmware version and 0.25mm nozzle profiles
+1.0.3 Added filament profiles
+1.0.2 Added SLA materials
+1.0.1 Updated MK3 firmware version check to 3.8.0, new soluble support profiles for 0.6mm nozzle diameter MMU2S printers.
+1.0.0 Updated end G-code for the MMU2 profiles to lift the extruder at the end of print. Wipe tower bridging distance was made smaller for soluble supports.
+1.0.0-beta1 Updated color for the ASA filaments to differ from the other filaments. Single extruder printers now have no extruder color assigned, obects and toolpaths will be colored with the color of the active filament.
+1.0.0-beta0 Printer model checks in start G-codes, ASA filament profiles, limits on min / max SL1 exposition times
+1.0.0-alpha2 Printer model and nozzle diameter check
+1.0.0-alpha1 Added Prusament ASA profile
+1.0.0-alpha0 Filament specific retract for PET and similar copolymers, and for FLEX
+min_slic3r_version = 1.42.0-alpha6
+0.8.11 Updated firmware version.
+0.8.10 Updated firmware version.
+0.8.9 Updated firmware version for MK2.5/S and MK3/S.
+0.8.8 Updated firmware version for MK2.5/S and MK3/S.
+0.8.7 Updated firmware version
+0.8.6 Updated firmware version for MK2.5/S and MK3/S
+0.8.5 Updated SL1 printer and material settings
+0.8.4 Added Prusament ASA profile
+0.8.3 FW version and SL1 materials update
+0.8.2 FFF and SL1 settings update
+0.8.1 Output settings and SLA materials update
+0.8.0 Updated for the PrusaSlicer 2.0.0 final release
+0.8.0-rc2 Updated firmware versions for MK2.5/S and MK3/S
+0.8.0-rc1 Updated SLA profiles
+0.8.0-rc Updated for the PrusaSlicer 2.0.0-rc release
+0.8.0-beta4 Updated SLA profiles
+0.8.0-beta3 Updated SLA profiles
+0.8.0-beta2 Updated SLA profiles
+0.8.0-beta1 Updated SLA profiles
+0.8.0-beta Updated SLA profiles
+0.8.0-alpha9 Updated SLA and FFF profiles
+0.8.0-alpha8 Updated SLA profiles
+0.8.0-alpha7 Updated SLA profiles
+0.8.0-alpha6 Updated SLA profiles
+min_slic3r_version = 1.42.0-alpha
+0.8.0-alpha Updated SLA profiles
+0.4.0-alpha4 Updated SLA profiles
+0.4.0-alpha3 Update of SLA profiles
+0.4.0-alpha2 First SLA profiles
+min_slic3r_version = 1.41.3-alpha
+0.4.12 Updated firmware version for MK2.5/S and MK3/S.
+0.4.11 Updated firmware version for MK2.5/S and MK3/S.
+0.4.10 Updated firmware version
+0.4.9 Updated firmware version for MK2.5/S and MK3/S
+0.4.8 MK2.5/3/S FW update
+0.4.7 MK2/S/MMU FW update
+0.4.6 Updated firmware versions for MK2.5/S and MK3/S
+0.4.5 Enabled remaining time support for MK2/S/MMU1
+0.4.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
+0.4.3 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
+0.4.2 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
+0.4.1 New MK2.5S and MK3S FW versions
+0.4.0 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt 
+min_slic3r_version = 1.41.1
+0.3.11 Updated firmware version for MK2.5/S and MK3/S.
+0.3.10 Updated firmware version
+0.3.9 Updated firmware version for MK2.5/S and MK3/S
+0.3.8 MK2.5/3/S FW update
+0.3.7 MK2/S/MMU FW update
+0.3.6 Updated firmware versions for MK2.5 and MK3
+0.3.5 New MK2.5 and MK3 FW versions
+0.3.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt 
+0.3.3 Prusament PETG released
+0.3.2 New MK2.5 and MK3 FW versions
+0.3.1 New MK2.5 and MK3 FW versions
+0.3.0 New MK2.5 and MK3 FW version
+min_slic3r_version = 1.41.0-alpha
+0.2.9 New MK2.5 and MK3 FW versions
+0.2.8 New MK2.5 and MK3 FW version
+min_slic3r_version = 1.41.1
+0.2.7 New MK2.5 and MK3 FW version
+0.2.6 Added MMU2 MK2.5 settings
+min_slic3r_version = 1.41.0-alpha
+0.2.5 Prusament is out - added prusament settings
+0.2.4 Added soluble support profiles for MMU2
+0.2.3 Added materials for MMU2 single mode, edited MK3 xy stealth feedrate limit
+0.2.2 Edited MMU2 Single mode purge line
+0.2.1 Added PET and BVOH settings for MMU2
+0.2.0-beta5 Fixed MMU1 ramming parameters
+0.2.0-beta4 Added filament loading speed at start, increased minimal purge on wipe tower
+0.2.0-beta3 Edited ramming parameters and filament cooling moves for MMU2
+0.2.0-beta2 Edited first layer speed and wipe tower position
+0.2.0-beta Removed limit on the MK3MMU2 height, added legacy M204 S T format to the MK2 profiles
+0.2.0-alpha8 Added filament_load/unload_time for the PLA/ABS MMU2 filament presets.
+0.2.0-alpha7 Vojtech's fix the incorrect *MK3* references
+0.2.0-alpha6 Jindra's way to fix the 0.2.0-alpha5 version
+0.2.0-alpha5 Bumped up firmware versions for MK2.5/MK3 to 3.3.1, disabled priming areas for MK3MMU2
+0.2.0-alpha4 Extended the custom start/end G-codes of the MMU2.0 printers for no priming towers.
+0.2.0-alpha3 Adjusted machine limits for time estimates, added filament density and cost
+0.2.0-alpha2 Renamed the key MK3SMMU to MK3MMU2, added a generic PLA MMU2 material
+0.2.0-alpha1 added initial profiles for the i3 MK3 Multi Material Upgrade 2.0
+0.2.0-alpha moved machine limits from the start G-code to the new print profile parameters
+min_slic3r_version = 1.40.0
+0.1.18 Updated firmware version
+0.1.17 Updated firmware version for MK2.5/S and MK3/S
+0.1.16 MK2.5/3/S FW update
+0.1.15 MK2/S/MMU FW update
+0.1.14 Updated firmware versions for MK2.5 and MK3
+0.1.13 New MK2.5 and MK3 FW versions
+0.1.12 New MK2.5 and MK3 FW versions
+0.1.11 fw version changed to 3.3.1
+0.1.10 MK3 jerk and acceleration update
+0.1.9 edited support extrusion width for 0.25 and 0.6 nozzles
+0.1.8 extrusion width for 0,25, 0.6 and variable layer height fixes
+0.1.7 Fixed errors in 0.25mm and 0.6mm profiles
+0.1.6 Split the MK2.5 profile from the MK2S
+min_slic3r_version = 1.40.0-beta
+0.1.5 fixed printer_variant fields for the i3 MK3 0.25 and 0.6mm nozzles
+0.1.4 edited fw version, added z-raise after print
+min_slic3r_version = 1.40.0-alpha
+0.1.3 Fixed an incorrect position of the max_print_height parameter
+0.1.2 Wipe tower changes
+0.1.1 Minor print speed adjustments
+0.1.0 Initial
diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini
index 47a3883b4..7d4ab7ccb 100644
--- a/resources/profiles/PrusaResearch.ini
+++ b/resources/profiles/PrusaResearch.ini
@@ -5,7 +5,7 @@
 name = Prusa Research
 # Configuration version of this file. Config file will only be installed, if the config_version differs.
 # This means, the server may force the PrusaSlicer configuration to be downgraded.
-config_version = 1.4.0-alpha7
+config_version = 1.4.0-alpha8
 # Where to get the updates from?
 config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
 changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
@@ -120,7 +120,7 @@ technology = SLA
 family = SL1
 bed_model = sl1_bed.stl
 bed_texture = sl1.svg
-default_materials = Prusa Orange Tough @0.05
+default_materials = Prusa Orange Tough @0.05; Prusament Resin Tough Prusa Orange @0.05
 
 [printer_model:SL1S]
 name = Original Prusa SL1S SPEED
@@ -129,7 +129,7 @@ technology = SLA
 family = SL1
 bed_model = sl1s_bed.stl
 bed_texture = sl1s.svg
-default_materials = Prusa Orange Tough @0.05 SL1S
+default_materials = Prusa Orange Tough @0.05 SL1S; Prusament Resin Tough Prusa Orange @0.05 SL1S
 
 # All presets starting with asterisk, for example *common*, are intermediate and they will
 # not make it into the user interface.
@@ -181,7 +181,7 @@ max_volumetric_extrusion_rate_slope_positive = 0
 max_volumetric_speed = 0
 min_skirt_length = 4
 notes = 
-overhangs = 0
+overhangs = 1
 only_retract_when_crossing_perimeters = 0
 ooze_prevention = 0
 output_filename_format = {input_filename_base}_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode
@@ -395,6 +395,7 @@ top_solid_min_thickness = 1.2
 bottom_solid_min_thickness = 0.8
 single_extruder_multi_material_priming = 0
 thick_bridges = 1
+overhangs = 0
 
 [print:*soluble_support*]
 overhangs = 1
@@ -460,10 +461,10 @@ bridge_flow_ratio = 1
 bridge_speed = 20
 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1
 layer_height = 0.1
-perimeter_acceleration = 800
+perimeter_acceleration = 600
 top_solid_layers = 9
 support_material_contact_distance = 0.17
-raft_contact_distance = 0.17
+raft_contact_distance = 0.15
 
 [print:*0.15mm*]
 inherits = *common*
@@ -619,6 +620,7 @@ support_material_contact_distance = 0.1
 raft_contact_distance = 0.2
 top_solid_infill_speed = 40
 thick_bridges = 1
+overhangs = 0
 
 ## MMU1 specific
 [print:0.15mm OPTIMAL SOLUBLE FULL]
@@ -704,7 +706,7 @@ small_perimeter_speed = 15
 solid_infill_speed = 40
 top_solid_infill_speed = 30
 support_material_contact_distance = 0.08
-raft_contact_distance = 0.08
+raft_contact_distance = 0.07
 
 ## MK2 - 0.6mm nozzle
 
@@ -772,6 +774,7 @@ single_extruder_multi_material_priming = 0
 inherits = 0.35mm FAST
 compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4
 single_extruder_multi_material_priming = 0
+overhangs = 0
 
 ## MK2.5 - MMU2 specific
 
@@ -1013,7 +1016,7 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and
 fill_pattern = grid
 fill_density = 20%
 support_material_contact_distance = 0.08
-raft_contact_distance = 0.08
+raft_contact_distance = 0.07
 
 ## MK3 - 0.6mm nozzle
 
@@ -1028,7 +1031,7 @@ perimeter_speed = 45
 solid_infill_speed = 70
 top_solid_infill_speed = 45
 support_material_contact_distance = 0.22
-raft_contact_distance = 0.22
+raft_contact_distance = 0.2
 bridge_flow_ratio = 1
 
 [print:0.20mm DETAIL @0.6 nozzle MK3]
@@ -1042,7 +1045,7 @@ perimeter_speed = 45
 solid_infill_speed = 70
 top_solid_infill_speed = 45
 support_material_contact_distance = 0.22
-raft_contact_distance = 0.22
+raft_contact_distance = 0.2
 bridge_flow_ratio = 1
 
 [print:0.30mm QUALITY @0.6 nozzle MK3]
@@ -1311,7 +1314,7 @@ compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and
 fill_pattern = grid
 fill_density = 20%
 support_material_contact_distance = 0.08
-raft_contact_distance = 0.08
+raft_contact_distance = 0.07
 
 # MINI - 0.6mm nozzle
 
@@ -4496,7 +4499,31 @@ initial_exposure_time = 35
 material_type = Tough
 material_vendor = Monocure
   
-## Prusa
+## Prusa Polymers 0.025
+
+[sla_material:Prusament Resin Tough Prusa Orange @0.025]
+inherits = *common 0.025*
+exposure_time = 5
+initial_exposure_time = 35
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Rich Black @0.025]
+inherits = *common 0.025*
+exposure_time = 5
+initial_exposure_time = 35
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Anthracite Grey @0.025]
+inherits = *common 0.025*
+exposure_time = 6
+initial_exposure_time = 35
+material_type = Tough
+material_vendor = Prusa Polymers
+  
+## Prusa 0.025
+  
 [sla_material:Prusa Orange Tough @0.025]
 inherits = *common 0.025*
 exposure_time = 6
@@ -5185,7 +5212,30 @@ initial_exposure_time = 35
 material_type = Tough
 material_vendor = Zortrax
 
-## Prusa
+## Prusa Polymers 0.05
+
+[sla_material:Prusament Resin Tough Prusa Orange @0.05]
+inherits = *common 0.05*
+exposure_time = 6
+initial_exposure_time = 35
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Rich Black @0.05]
+inherits = *common 0.05*
+exposure_time = 6
+initial_exposure_time = 35
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Anthracite Grey @0.05]
+inherits = *common 0.05*
+exposure_time = 7
+initial_exposure_time = 35
+material_type = Tough
+material_vendor = Prusa Polymers
+
+## Prusa 0.05
 
 [sla_material:Prusa Beige Tough @0.05]
 inherits = *common 0.05*
@@ -5447,7 +5497,30 @@ initial_exposure_time = 50
 material_type = Tough
 material_vendor = BlueCast
 
-## Prusa
+## Prusa Polymers 0.1
+
+[sla_material:Prusament Resin Tough Prusa Orange @0.1]
+inherits = *common 0.1*
+exposure_time = 13
+initial_exposure_time = 45
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Rich Black @0.1]
+inherits = *common 0.1*
+exposure_time = 13
+initial_exposure_time = 45
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Anthracite Grey @0.1]
+inherits = *common 0.1*
+exposure_time = 14
+initial_exposure_time = 45
+material_type = Tough
+material_vendor = Prusa Polymers
+
+## Prusa 0.1
 
 [sla_material:Prusa Orange Tough @0.1]
 inherits = *common 0.1*
@@ -5530,6 +5603,31 @@ material_vendor = Made for Prusa
 
 ## 0.025 SL1S
 
+## Prusa Polymers 0.025
+
+[sla_material:Prusament Resin Tough Prusa Orange @0.025 SL1S]
+inherits = *0.025_sl1s*
+exposure_time = 1.8
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Rich Black @0.025 SL1S]
+inherits = *0.025_sl1s*
+exposure_time = 1.8
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Anthracite Grey @0.025 SL1S]
+inherits = *0.025_sl1s*
+exposure_time = 2
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+## Made for Prusa 0.025
+
 [sla_material:Prusa Orange Tough @0.025 SL1S]
 inherits = *0.025_sl1s*
 exposure_time = 1.8
@@ -5644,6 +5742,31 @@ material_vendor = Peopoly
 
 ## 0.05 SL1S
 
+## Prusa Polymers 0.05
+
+[sla_material:Prusament Resin Tough Prusa Orange @0.05 SL1S]
+inherits = *0.05_sl1s*
+exposure_time = 2
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Rich Black @0.05 SL1S]
+inherits = *0.05_sl1s*
+exposure_time = 2
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Anthracite Grey @0.05 SL1S]
+inherits = *0.05_sl1s*
+exposure_time = 2.4
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+## Made for Prusa 0.05
+
 [sla_material:Prusa Orange Tough @0.05 SL1S]
 inherits = *0.05_sl1s*
 exposure_time = 2
@@ -5758,6 +5881,31 @@ material_vendor = Peopoly
 
 ## 0.1 SL1S
 
+## Prusa Polymers 0.1
+
+[sla_material:Prusament Resin Tough Prusa Orange @0.1 SL1S]
+inherits = *0.1_sl1s*
+exposure_time = 2.6
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Rich Black @0.1 SL1S]
+inherits = *0.1_sl1s*
+exposure_time = 2.6
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+[sla_material:Prusament Resin Tough Anthracite Grey @0.1 SL1S]
+inherits = *0.1_sl1s*
+exposure_time = 3
+initial_exposure_time = 25
+material_type = Tough
+material_vendor = Prusa Polymers
+
+## Made for Prusa 0.1
+
 [sla_material:Prusa Orange Tough @0.1 SL1S]
 inherits = *0.1_sl1s*
 exposure_time = 2.6
@@ -6573,7 +6721,7 @@ nozzle_diameter = 0.6
 max_layer_height = 0.40
 min_layer_height = 0.15
 default_print_profile = 0.30mm QUALITY @0.6 nozzle MINI
-retract_length = 3.5
+retract_length = 3.2
 retract_before_travel = 1.5
 
 [printer:Original Prusa MINI & MINI+ 0.8 nozzle]
diff --git a/resources/shaders/gouraud_light_instanced.vs b/resources/shaders/gouraud_light_instanced.vs
index 997b6a2bf..a42f8e9a4 100644
--- a/resources/shaders/gouraud_light_instanced.vs
+++ b/resources/shaders/gouraud_light_instanced.vs
@@ -34,9 +34,7 @@ void main()
     float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
 
     intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
-	float width = 1.5 * i_scales.x;
-	float height = 1.5 * i_scales.y;
-    vec4 world_position = vec4(v_position * vec3(vec2(width), height) + i_offset - vec3(0.0, 0.0, 0.5 * i_scales.y), 1.0);
+    vec4 world_position = vec4(v_position * vec3(vec2(1.5 * i_scales.x), 1.5 * i_scales.y) + i_offset - vec3(0.0, 0.0, 0.5 * i_scales.y), 1.0);
     vec3 eye_position = (gl_ModelViewMatrix * world_position).xyz;
     intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
 
diff --git a/sandboxes/opencsg/Engine.cpp b/sandboxes/opencsg/Engine.cpp
index f110b23c5..53e340294 100644
--- a/sandboxes/opencsg/Engine.cpp
+++ b/sandboxes/opencsg/Engine.cpp
@@ -145,7 +145,7 @@ void IndexedVertexArray::load_mesh(const TriangleMesh &mesh)
     this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count());
     
     int vertices_count = 0;
-    for (size_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) {
+    for (size_t i = 0; i < mesh.facets_count(); ++i) {
         const stl_facet &facet = mesh.stl.facet_start[i];
         for (int j = 0; j < 3; ++j)
             this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2));
diff --git a/src/admesh/stl.h b/src/admesh/stl.h
index 1ca75c95d..33e2b9c94 100644
--- a/src/admesh/stl.h
+++ b/src/admesh/stl.h
@@ -244,9 +244,15 @@ inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::Don
 	stl_get_size(stl);
 }
 
+template<typename V>
+inline void its_translate(indexed_triangle_set &its, const V v)
+{
+  for (stl_vertex &v_dst : its.vertices)
+    v_dst += v;
+}
 
 template<typename T>
-extern void its_transform(indexed_triangle_set &its, T *trafo3x4)
+inline void its_transform(indexed_triangle_set &its, T *trafo3x4)
 {
 	for (stl_vertex &v_dst : its.vertices) {
 		stl_vertex  v_src = v_dst;
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index f9bef903b..d42077b9c 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -78,8 +78,6 @@ add_library(libslic3r STATIC
     Format/OBJ.hpp
     Format/objparser.cpp
     Format/objparser.hpp
-    Format/PRUS.cpp
-    Format/PRUS.hpp
     Format/STL.cpp
     Format/STL.hpp
     Format/SL1.hpp
diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index 295c1413d..043f951ef 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -21,6 +21,7 @@
 #include <boost/algorithm/string/replace.hpp>
 #include <boost/filesystem/operations.hpp>
 #include <boost/spirit/include/karma.hpp>
+#include <boost/spirit/include/qi_int.hpp>
 #include <boost/log/trivial.hpp>
 
 #include <boost/property_tree/ptree.hpp>
@@ -32,6 +33,8 @@ namespace pt = boost::property_tree;
 #include <Eigen/Dense>
 #include "miniz_extension.hpp"
 
+#include <fast_float/fast_float.h>
+
 // Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
 // https://github.com/boostorg/spirit/pull/586
 // where the exported string is one digit shorter than it should be to guarantee lossless round trip.
@@ -172,14 +175,18 @@ std::string get_attribute_value_string(const char** attributes, unsigned int att
 
 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;
+    float value = 0.0f;
+    if (const char *text = get_attribute_value_charptr(attributes, attributes_size, attribute_key); text != nullptr)
+        fast_float::from_chars(text, text + strlen(text), value);
+    return value;
 }
 
 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;
+    int value = 0;
+    if (const char *text = get_attribute_value_charptr(attributes, attributes_size, attribute_key); text != nullptr)
+        boost::spirit::qi::parse(text, text + strlen(text), boost::spirit::qi::int_, value);
+    return value;
 }
 
 bool get_attribute_value_bool(const char** attributes, unsigned int attributes_size, const char* attribute_key)
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index cebc9136f..bff894558 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -1842,11 +1842,7 @@ namespace ProcessLayer
                 assert(m600_extruder_before_layer >= 0);
 		        // Color Change or Tool Change as Color Change.
                 // add tag for processor
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
                 gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(m600_extruder_before_layer) + "," + custom_gcode->color + "\n";
-#else
-                gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(m600_extruder_before_layer) + "\n";
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
 
                 if (!single_extruder_printer && m600_extruder_before_layer >= 0 && first_extruder_id != (unsigned)m600_extruder_before_layer
                     // && !MMU1
diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index d16cfb9cf..d26e085e4 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -27,9 +27,7 @@ static const float DEFAULT_TOOLPATH_HEIGHT = 0.2f;
 static const float INCHES_TO_MM = 25.4f;
 static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
 static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
-#if ENABLE_RETRACT_ACCELERATION
 static const float DEFAULT_RETRACT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
-#endif // ENABLE_RETRACT_ACCELERATION
 static const float DEFAULT_TRAVEL_ACCELERATION = 1250.0f;
 
 static const size_t MIN_EXTRUDERS_COUNT = 5;
@@ -184,10 +182,8 @@ void GCodeProcessor::TimeMachine::reset()
     enabled = false;
     acceleration = 0.0f;
     max_acceleration = 0.0f;
-#if ENABLE_RETRACT_ACCELERATION
     retract_acceleration = 0.0f;
     max_retract_acceleration = 0.0f;
-#endif // ENABLE_RETRACT_ACCELERATION
     travel_acceleration = 0.0f;
     max_travel_acceleration = 0.0f;
     extrude_factor_override_percentage = 1.0f;
@@ -740,9 +736,7 @@ void GCodeProcessor::Result::reset() {
     extruder_colors = std::vector<std::string>();
     filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
     filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     time = 0;
 }
 #else
@@ -756,9 +750,7 @@ void GCodeProcessor::Result::reset() {
     extruder_colors = std::vector<std::string>();
     filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
     filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
 }
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
 
@@ -895,11 +887,9 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
         float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
         m_time_processor.machines[i].max_acceleration = max_acceleration;
         m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
-#if ENABLE_RETRACT_ACCELERATION
         float max_retract_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i);
         m_time_processor.machines[i].max_retract_acceleration = max_retract_acceleration;
         m_time_processor.machines[i].retract_acceleration = (max_retract_acceleration > 0.0f) ? max_retract_acceleration : DEFAULT_RETRACT_ACCELERATION;
-#endif // ENABLE_RETRACT_ACCELERATION
         float max_travel_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_travel, i);
         m_time_processor.machines[i].max_travel_acceleration = max_travel_acceleration;
         m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
@@ -1117,11 +1107,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
         float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
         m_time_processor.machines[i].max_acceleration = max_acceleration;
         m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
-#if ENABLE_RETRACT_ACCELERATION
         float max_retract_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i);
         m_time_processor.machines[i].max_retract_acceleration = max_retract_acceleration;
         m_time_processor.machines[i].retract_acceleration = (max_retract_acceleration > 0.0f) ? max_retract_acceleration : DEFAULT_RETRACT_ACCELERATION;
-#endif // ENABLE_RETRACT_ACCELERATION
         float max_travel_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_travel, i);
         m_time_processor.machines[i].max_travel_acceleration = max_travel_acceleration;
         m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
@@ -1200,9 +1188,7 @@ void GCodeProcessor::reset()
     m_result.id = ++s_result_id;
 
     m_use_volumetric_e = false;
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     m_last_default_color_id = 0;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
 
 #if ENABLE_FIX_PREVIEW_OPTIONS_Z
     m_options_z_corrector.reset();
@@ -1741,7 +1727,6 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
     // color change tag
     if (boost::starts_with(comment, reserved_tag(ETags::Color_Change))) {
         unsigned char extruder_id = 0;
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
         static std::vector<std::string> Default_Colors = {
             "#0B2C7A", // { 0.043f, 0.173f, 0.478f }, // bluish
             "#1C8891", // { 0.110f, 0.533f, 0.569f },
@@ -1790,16 +1775,6 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
             if (m_last_default_color_id == Default_Colors.size())
                 m_last_default_color_id = 0;
         }
-#else
-        if (boost::starts_with(comment.substr(reserved_tag(ETags::Color_Change).size()), ",T")) {
-            int eid;
-            if (!parse_number(comment.substr(reserved_tag(ETags::Color_Change).size() + 2), eid) || eid < 0 || eid > 255) {
-                BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Color_Change (" << comment << ").";
-                return;
-            }
-            extruder_id = static_cast<unsigned char>(eid);
-        }
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
 
         if (extruder_id < m_extruder_colors.size())
             m_extruder_colors[extruder_id] = static_cast<unsigned char>(m_extruder_offsets.size()) + m_cp_color.counter; // color_change position in list of color for preview
@@ -1810,7 +1785,6 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
         if (m_extruder_id == extruder_id) {
             m_cp_color.current = m_extruder_colors[extruder_id];
             store_move_vertex(EMoveType::Color_change);
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
             CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::ColorChange, extruder_id + 1, color, "" };
             m_result.custom_gcode_per_print_z.emplace_back(item);
 #if ENABLE_FIX_PREVIEW_OPTIONS_Z
@@ -1818,27 +1792,19 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
 #endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
             process_custom_gcode_time(CustomGCode::ColorChange);
             process_filaments(CustomGCode::ColorChange);
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
         }
 
-#if !ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
-        process_custom_gcode_time(CustomGCode::ColorChange);
-        process_filaments(CustomGCode::ColorChange);
-#endif // !ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
-
         return;
     }
 
     // pause print tag
     if (comment == reserved_tag(ETags::Pause_Print)) {
         store_move_vertex(EMoveType::Pause_Print);
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
         CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::PausePrint, m_extruder_id + 1, "", "" };
         m_result.custom_gcode_per_print_z.emplace_back(item);
 #if ENABLE_FIX_PREVIEW_OPTIONS_Z
         m_options_z_corrector.set();
 #endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
         process_custom_gcode_time(CustomGCode::PausePrint);
         return;
     }
@@ -1846,13 +1812,11 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
     // custom code tag
     if (comment == reserved_tag(ETags::Custom_Code)) {
         store_move_vertex(EMoveType::Custom_GCode);
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
         CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::Custom, m_extruder_id + 1, "", "" };
         m_result.custom_gcode_per_print_z.emplace_back(item);
 #if ENABLE_FIX_PREVIEW_OPTIONS_Z
         m_options_z_corrector.set();
 #endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
         return;
     }
 
@@ -2943,22 +2907,14 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
                 set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
                 set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
                 if (line.has_value('T', value))
-#if ENABLE_RETRACT_ACCELERATION
                     set_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
-#else
-                    set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
-#endif // ENABLE_RETRACT_ACCELERATION
             }
             else {
                 // New acceleration format, compatible with the upstream Marlin.
                 if (line.has_value('P', value))
                     set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
                 if (line.has_value('R', value))
-#if ENABLE_RETRACT_ACCELERATION
                     set_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
-#else
-                    set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
-#endif // ENABLE_RETRACT_ACCELERATION
                 if (line.has_value('T', value))
                     // Interpret the T value as the travel acceleration in the new Marlin format.
                     set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
@@ -3216,20 +3172,12 @@ float GCodeProcessor::get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode
     }
 }
 
-#if ENABLE_RETRACT_ACCELERATION
 float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
 {
     size_t id = static_cast<size_t>(mode);
     return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].retract_acceleration : DEFAULT_RETRACT_ACCELERATION;
 }
-#else
-float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
-{
-    return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, static_cast<size_t>(mode));
-}
-#endif // ENABLE_RETRACT_ACCELERATION
 
-#if ENABLE_RETRACT_ACCELERATION
 void GCodeProcessor::set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value)
 {
     size_t id = static_cast<size_t>(mode);
@@ -3239,7 +3187,6 @@ void GCodeProcessor::set_retract_acceleration(PrintEstimatedStatistics::ETimeMod
             std::min(value, m_time_processor.machines[id].max_retract_acceleration);
     }
 }
-#endif // ENABLE_RETRACT_ACCELERATION
 
 float GCodeProcessor::get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
 {
diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp
index 040f7432a..5af040307 100644
--- a/src/libslic3r/GCode/GCodeProcessor.hpp
+++ b/src/libslic3r/GCode/GCodeProcessor.hpp
@@ -242,11 +242,9 @@ namespace Slic3r {
             float acceleration; // mm/s^2
             // hard limit for the acceleration, to which the firmware will clamp.
             float max_acceleration; // mm/s^2
-#if ENABLE_RETRACT_ACCELERATION
             float retract_acceleration; // mm/s^2
             // hard limit for the acceleration, to which the firmware will clamp.
             float max_retract_acceleration; // mm/s^2
-#endif // ENABLE_RETRACT_ACCELERATION
             float travel_acceleration; // mm/s^2
             // hard limit for the travel acceleration, to which the firmware will clamp.
             float max_travel_acceleration; // mm/s^2
@@ -359,9 +357,7 @@ namespace Slic3r {
             std::vector<float> filament_diameters;
             std::vector<float> filament_densities;
             PrintEstimatedStatistics print_statistics;
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
             std::vector<CustomGCode::Item> custom_gcode_per_print_z;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
 
 #if ENABLE_GCODE_VIEWER_STATISTICS
             int64_t time{ 0 };
@@ -536,9 +532,7 @@ namespace Slic3r {
 #if ENABLE_FIX_PREVIEW_OPTIONS_Z
         OptionsZCorrector m_options_z_corrector;
 #endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
         size_t m_last_default_color_id;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
 #if ENABLE_GCODE_VIEWER_STATISTICS
         std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
 #endif // ENABLE_GCODE_VIEWER_STATISTICS
@@ -724,9 +718,7 @@ namespace Slic3r {
         float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
         float get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
         float get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
-#if ENABLE_RETRACT_ACCELERATION
         void  set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
-#endif // ENABLE_RETRACT_ACCELERATION
         float get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
         void  set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
         float get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
diff --git a/src/libslic3r/LocalesUtils.cpp b/src/libslic3r/LocalesUtils.cpp
index 64ab700ed..8c42a36ba 100644
--- a/src/libslic3r/LocalesUtils.cpp
+++ b/src/libslic3r/LocalesUtils.cpp
@@ -1,7 +1,13 @@
 #include "LocalesUtils.hpp"
 
+#ifdef _WIN32
+    #include <charconv>
+#endif
 #include <stdexcept>
 
+#include <fast_float/fast_float.h>
+
+
 namespace Slic3r {
 
 
@@ -11,15 +17,15 @@ CNumericLocalesSetter::CNumericLocalesSetter()
     _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
     m_orig_numeric_locale = std::setlocale(LC_NUMERIC, nullptr);
     std::setlocale(LC_NUMERIC, "C");
-#elif __linux__
+#elif __APPLE__
+    m_original_locale = uselocale((locale_t)0);
+    m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_original_locale);
+    uselocale(m_new_locale);
+#else // linux / BSD
     m_original_locale = uselocale((locale_t)0);
     m_new_locale = duplocale(m_original_locale);
     m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_new_locale);
     uselocale(m_new_locale);
-#else // APPLE
-    m_original_locale = uselocale((locale_t)0);
-    m_new_locale = newlocale(LC_NUMERIC_MASK, "C", m_original_locale);
-    uselocale(m_new_locale);
 #endif
 }
 
@@ -48,25 +54,34 @@ bool is_decimal_separator_point()
 double string_to_double_decimal_point(const std::string& str, size_t* pos /* = nullptr*/)
 {
     double out;
-    std::istringstream stream(str);
-    if (! (stream >> out))
-        throw std::invalid_argument("string_to_double_decimal_point conversion failed.");
-    if (pos) {
-        if (stream.eof())
-            *pos = str.size();
-        else
-            *pos = stream.tellg();
-    }
+    size_t p = fast_float::from_chars(str.data(), str.data() + str.size(), out).ptr - str.data();
+    if (pos)
+        *pos = p;
     return out;
 }
 
 std::string float_to_string_decimal_point(double value, int precision/* = -1*/)
 {
+    // Our Windows build server fully supports C++17 std::to_chars. Let's use it.
+    // Other platforms are behind, fall back to slow stringstreams for now.
+#ifdef _WIN32
+    constexpr size_t SIZE = 20;
+    char out[SIZE] = "";
+    std::to_chars_result res;
+    if (precision >=0)
+        res = std::to_chars(out, out+SIZE, value, std::chars_format::fixed, precision);
+    else
+        res = std::to_chars(out, out+SIZE, value, std::chars_format::general, 6);
+    if (res.ec == std::errc::value_too_large)
+        throw std::invalid_argument("float_to_string_decimal_point conversion failed.");
+    return std::string(out, res.ptr - out);
+#else
     std::stringstream buf;
     if (precision >= 0)
         buf << std::fixed << std::setprecision(precision);
     buf << value;
     return buf.str();
+#endif
 }
 
 
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index fb6ab5635..2844f644c 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -9,7 +9,6 @@
 
 #include "Format/AMF.hpp"
 #include "Format/OBJ.hpp"
-#include "Format/PRUS.hpp"
 #include "Format/STL.hpp"
 #include "Format/3mf.hpp"
 
@@ -118,8 +117,6 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
     else if (boost::algorithm::iends_with(input_file, ".3mf"))
         //FIXME options & LoadAttribute::CheckVersion ? 
         result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, false);
-    else if (boost::algorithm::iends_with(input_file, ".prusa"))
-        result = load_prus(input_file.c_str(), &model);
     else
         throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension.");
 
@@ -1154,7 +1151,7 @@ size_t ModelObject::facets_count() const
     size_t num = 0;
     for (const ModelVolume *v : this->volumes)
         if (v->is_model_part())
-            num += v->mesh().stl.stats.number_of_facets;
+            num += v->mesh().facets_count();
     return num;
 }
 
@@ -1508,9 +1505,9 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const
 
         const Transform3d mv = mi * v->get_matrix();
         const TriangleMesh& hull = v->get_convex_hull();
-		for (const stl_facet &facet : hull.stl.facet_start)
+        for (const stl_triangle_vertex_indices& facet : hull.its.indices)
 			for (int i = 0; i < 3; ++ i)
-				min_z = std::min(min_z, (mv * facet.vertex[i].cast<double>()).z());
+				min_z = std::min(min_z, (mv * hull.its.vertices[facet[i]].cast<double>()).z());
     }
 
     return min_z + inst->get_offset(Z);
@@ -1529,9 +1526,9 @@ double ModelObject::get_instance_max_z(size_t instance_idx) const
 
         const Transform3d mv = mi * v->get_matrix();
         const TriangleMesh& hull = v->get_convex_hull();
-        for (const stl_facet& facet : hull.stl.facet_start)
+        for (const stl_triangle_vertex_indices& facet : hull.its.indices)
             for (int i = 0; i < 3; ++i)
-                max_z = std::max(max_z, (mv * facet.vertex[i].cast<double>()).z());
+                max_z = std::max(max_z, (mv * hull.its.vertices[facet[i]].cast<double>()).z());
     }
 
     return max_z + inst->get_offset(Z);
@@ -1584,27 +1581,27 @@ void ModelObject::print_info() const
     cout << "max_x = " << bb.max(0) << endl;
     cout << "max_y = " << bb.max(1) << endl;
     cout << "max_z = " << bb.max(2) << endl;
-    cout << "number_of_facets = " << mesh.stl.stats.number_of_facets  << endl;
+    cout << "number_of_facets = " << mesh.facets_count() << endl;
     cout << "manifold = "   << (mesh.is_manifold() ? "yes" : "no") << endl;
     
     mesh.repair();  // this calculates number_of_parts
     if (mesh.needed_repair()) {
         mesh.repair();
-        if (mesh.stl.stats.degenerate_facets > 0)
-            cout << "degenerate_facets = "  << mesh.stl.stats.degenerate_facets << endl;
-        if (mesh.stl.stats.edges_fixed > 0)
-            cout << "edges_fixed = "        << mesh.stl.stats.edges_fixed       << endl;
-        if (mesh.stl.stats.facets_removed > 0)
-            cout << "facets_removed = "     << mesh.stl.stats.facets_removed    << endl;
-        if (mesh.stl.stats.facets_added > 0)
-            cout << "facets_added = "       << mesh.stl.stats.facets_added      << endl;
-        if (mesh.stl.stats.facets_reversed > 0)
-            cout << "facets_reversed = "    << mesh.stl.stats.facets_reversed   << endl;
-        if (mesh.stl.stats.backwards_edges > 0)
-            cout << "backwards_edges = "    << mesh.stl.stats.backwards_edges   << endl;
+        if (mesh.stats().degenerate_facets > 0)
+            cout << "degenerate_facets = "  << mesh.stats().degenerate_facets << endl;
+        if (mesh.stats().edges_fixed > 0)
+            cout << "edges_fixed = "        << mesh.stats().edges_fixed       << endl;
+        if (mesh.stats().facets_removed > 0)
+            cout << "facets_removed = "     << mesh.stats().facets_removed    << endl;
+        if (mesh.stats().facets_added > 0)
+            cout << "facets_added = "       << mesh.stats().facets_added      << endl;
+        if (mesh.stats().facets_reversed > 0)
+            cout << "facets_reversed = "    << mesh.stats().facets_reversed   << endl;
+        if (mesh.stats().backwards_edges > 0)
+            cout << "backwards_edges = "    << mesh.stats().backwards_edges   << endl;
     }
-    cout << "number_of_parts =  " << mesh.stl.stats.number_of_parts << endl;
-    cout << "volume = "           << mesh.volume()                  << endl;
+    cout << "number_of_parts =  " << mesh.stats().number_of_parts << endl;
+    cout << "volume = "           << mesh.volume()                << endl;
 }
 
 std::string ModelObject::get_export_filename() const
@@ -1630,7 +1627,7 @@ std::string ModelObject::get_export_filename() const
 stl_stats ModelObject::get_object_stl_stats() const
 {
     if (this->volumes.size() == 1)
-        return this->volumes[0]->mesh().stl.stats;
+        return this->volumes[0]->mesh().stats();
 
     stl_stats full_stats;
     full_stats.volume = 0.f;
@@ -1638,7 +1635,7 @@ stl_stats ModelObject::get_object_stl_stats() const
     // fill full_stats from all objet's meshes
     for (ModelVolume* volume : this->volumes)
     {
-        const stl_stats& stats = volume->mesh().stl.stats;
+        const stl_stats& stats = volume->mesh().stats();
 
         // initialize full_stats (for repaired errors)
         full_stats.degenerate_facets    += stats.degenerate_facets;
@@ -1734,7 +1731,7 @@ void ModelVolume::calculate_convex_hull()
 
 int ModelVolume::get_mesh_errors_count() const
 {
-    const stl_stats& stats = this->mesh().stl.stats;
+    const stl_stats &stats = this->mesh().stats();
 
     return  stats.degenerate_facets + stats.edges_fixed     + stats.facets_removed +
             stats.facets_added      + stats.facets_reversed + stats.backwards_edges;
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index ba3156139..07274d352 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -774,7 +774,7 @@ private:
         assert(this->id() != this->supported_facets.id());
         assert(this->id() != this->seam_facets.id());
         assert(this->id() != this->mmu_segmentation_facets.id());
-        if (mesh.stl.stats.number_of_facets > 1)
+        if (mesh.facets_count() > 1)
             calculate_convex_hull();
     }
     ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull, ModelVolumeType type = ModelVolumeType::MODEL_PART) :
@@ -830,7 +830,7 @@ private:
         assert(this->config.id() == other.config.id());
         this->set_material_id(other.material_id());
         this->config.set_new_unique_id();
-        if (mesh.stl.stats.number_of_facets > 1)
+        if (mesh.facets_count() > 1)
             calculate_convex_hull();
 		assert(this->config.id().valid()); 
         assert(this->config.id() != other.config.id()); 
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index c673f8810..b33e8bda2 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -434,6 +434,8 @@ static inline bool sequential_print_vertical_clearance_valid(const Print &print)
     return it == print_instances_ordered.end() || (*it)->print_object->height() <= scale_(print.config().extruder_clearance_height.value);
 }
 
+
+
 // Precondition: Print::validate() requires the Print::apply() to be called its invocation.
 std::string Print::validate(std::string* warning) const
 {
@@ -526,42 +528,11 @@ std::string Print::validate(std::string* warning) const
             }
 
             if (has_custom_layering) {
-                const std::vector<coordf_t> &layer_height_profile_tallest = layer_height_profiles[tallest_object_idx];
                 for (size_t idx_object = 0; idx_object < m_objects.size(); ++ idx_object) {
                     if (idx_object == tallest_object_idx)
                         continue;
-                    const std::vector<coordf_t> &layer_height_profile = layer_height_profiles[idx_object];
-
-                    // The comparison of the profiles is not just about element-wise equality, some layers may not be
-                    // explicitely included. Always remember z and height of last reference layer that in the vector
-                    // and compare to that. In case some layers are in the vectors multiple times, only the last entry is
-                    // taken into account and compared.
-                    size_t i = 0; // index into tested profile
-                    size_t j = 0; // index into reference profile
-                    coordf_t ref_z = -1.;
-                    coordf_t next_ref_z = layer_height_profile_tallest[0];
-                    coordf_t ref_height = -1.;
-                    while (i < layer_height_profile.size()) {
-                        coordf_t this_z = layer_height_profile[i];
-                        // find the last entry with this z
-                        while (i+2 < layer_height_profile.size() && layer_height_profile[i+2] == this_z)
-                            i += 2;
-
-                        coordf_t this_height = layer_height_profile[i+1];
-                        if (ref_height < -1. || next_ref_z < this_z + EPSILON) {
-                            ref_z = next_ref_z;
-                            do { // one layer can be in the vector several times
-                                ref_height = layer_height_profile_tallest[j+1];
-                                if (j+2 >= layer_height_profile_tallest.size())
-                                    break;
-                                j += 2;
-                                next_ref_z = layer_height_profile_tallest[j];
-                            } while (ref_z == next_ref_z);
-                        }
-                        if (std::abs(this_height - ref_height) > EPSILON)
-                            return L("The Wipe tower is only supported if all objects have the same variable layer height");
-                        i += 2;
-                    }
+                    if (layer_height_profiles[idx_object] != layer_height_profiles[tallest_object_idx])
+                        return L("The Wipe tower is only supported if all objects have the same variable layer height");
                 }
             }
         }
diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp
index 1d61be85b..7db0de626 100644
--- a/src/libslic3r/PrintObjectSlice.cpp
+++ b/src/libslic3r/PrintObjectSlice.cpp
@@ -39,23 +39,6 @@ LayerPtrs new_layers(
     return out;
 }
 
-//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
-// This function will go away once we get rid of admesh from ModelVolume.
-static indexed_triangle_set get_mesh_its_fix_mesh_connectivity(TriangleMesh mesh)
-{
-    assert(mesh.repaired && mesh.has_shared_vertices());
-    if (mesh.stl.stats.number_of_facets > 0) {
-        assert(mesh.repaired && mesh.has_shared_vertices());
-        auto nr_degenerated = mesh.stl.stats.degenerate_facets;
-        stl_check_facets_exact(&mesh.stl);
-        if (nr_degenerated != mesh.stl.stats.degenerate_facets)
-            // stl_check_facets_exact() removed some newly degenerated faces. Some faces could become degenerate after some mesh transformation.
-            stl_generate_shared_vertices(&mesh.stl, mesh.its);
-    } else
-        mesh.its.clear();
-    return std::move(mesh.its);
-}
-
 // Slice single triangle mesh.
 static std::vector<ExPolygons> slice_volume(
     const ModelVolume             &volume,
@@ -65,7 +48,7 @@ static std::vector<ExPolygons> slice_volume(
 {
     std::vector<ExPolygons> layers;
     if (! zs.empty()) {
-        indexed_triangle_set its = get_mesh_its_fix_mesh_connectivity(volume.mesh());
+        indexed_triangle_set its = volume.mesh().its;
         if (its.indices.size() > 0) {
             MeshSlicingParamsEx params2 { params };
             params2.trafo = params2.trafo * volume.get_matrix();
diff --git a/src/libslic3r/SlicingAdaptive.cpp b/src/libslic3r/SlicingAdaptive.cpp
index 9b9b48f88..30ff67bba 100644
--- a/src/libslic3r/SlicingAdaptive.cpp
+++ b/src/libslic3r/SlicingAdaptive.cpp
@@ -35,13 +35,6 @@ legend("tan(a) as cura - topographic lines distance limit", "sqrt(tan(a)) as Pru
 namespace Slic3r
 {
 
-static inline std::pair<float, float> face_z_span(const stl_facet &f)
-{
-	return std::pair<float, float>(
-		std::min(std::min(f.vertex[0](2), f.vertex[1](2)), f.vertex[2](2)),
-		std::max(std::max(f.vertex[0](2), f.vertex[1](2)), f.vertex[2](2)));
-}
-
 // By Florens Waserfall aka @platch:
 // This constant essentially describes the volumetric error at the surface which is induced 
 // by stacking "elliptic" extrusion threads. It is empirically determined by
@@ -88,10 +81,15 @@ void SlicingAdaptive::prepare(const ModelObject &object)
     mesh.transform(first_instance.get_matrix(), first_instance.is_left_handed());
 
     // 1) Collect faces from mesh.
-    m_faces.reserve(mesh.stl.stats.number_of_facets);
-    for (const stl_facet &face : mesh.stl.facet_start) {
-    	Vec3f n = face.normal.normalized();
-		m_faces.emplace_back(FaceZ({ face_z_span(face), std::abs(n.z()), std::sqrt(n.x() * n.x() + n.y() * n.y()) }));
+    m_faces.reserve(mesh.facets_count());
+	for (stl_triangle_vertex_indices face : mesh.its.indices) {
+		stl_vertex vertex[3] = { mesh.its.vertices[face[0]], mesh.its.vertices[face[1]], mesh.its.vertices[face[2]] };
+		stl_vertex n         = face_normal_normalized(vertex);
+		std::pair<float, float> face_z_span {
+			std::min(std::min(vertex[0].z(), vertex[1].z()), vertex[2].z()),
+			std::max(std::max(vertex[0].z(), vertex[1].z()), vertex[2].z())
+		};
+		m_faces.emplace_back(FaceZ({ face_z_span, std::abs(n.z()), std::sqrt(n.x() * n.x() + n.y() * n.y()) }));
     }
 
 	// 2) Sort faces lexicographically by their Z span.
diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 6c07f436b..2da90ce9e 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -41,14 +41,6 @@
 //====================
 #define ENABLE_2_4_0_ALPHA1 1
 
-// Enable delayed rendering of transparent volumes
-#define ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_2_4_0_ALPHA1)
-// Enable the fix of importing color print view from gcode files into GCodeViewer
-#define ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER (1 && ENABLE_2_4_0_ALPHA1)
-// Enable drawing contours, at cut level, for sinking volumes
-#define ENABLE_SINKING_CONTOURS (1 && ENABLE_2_4_0_ALPHA1)
-// Enable implementation of retract acceleration in gcode processor
-#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA1)
 // Enable the fix for exporting and importing to/from 3mf file of mirrored volumes
 #define ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT (1 && ENABLE_2_4_0_ALPHA1)
 // Enable rendering seams (and other options) in preview using models
@@ -65,7 +57,7 @@
 // Enable rendering seams (and other options) in preview using batched models on systems not supporting OpenGL 3.3
 #define ENABLE_SEAMS_USING_BATCHED_MODELS (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_2_4_0_ALPHA2)
 // Enable fixing the z position of color change, pause print and custom gcode markers in preview
-#define ENABLE_FIX_PREVIEW_OPTIONS_Z (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER && ENABLE_2_4_0_ALPHA2)
+#define ENABLE_FIX_PREVIEW_OPTIONS_Z (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_2_4_0_ALPHA2)
 // Enable replacing a missing file during reload from disk command
 #define ENABLE_RELOAD_FROM_DISK_REPLACE_FILE (1 && ENABLE_2_4_0_ALPHA2)
 // Enable changes in preview layout
diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp
index fa8f8bce6..d289fca14 100644
--- a/src/libslic3r/TriangleMesh.cpp
+++ b/src/libslic3r/TriangleMesh.cpp
@@ -187,6 +187,10 @@ void TriangleMesh::repair(bool update_shared_vertices)
 
     this->repaired = true;
 
+    //FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
+    if (auto nr_degenerated = this->stl.stats.degenerate_facets; this->facets_count() > 0 && nr_degenerated > 0)
+        stl_check_facets_exact(&this->stl);
+
     BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
 
     // This call should be quite cheap, a lot of code requires the indexed_triangle_set data structure,
@@ -1129,15 +1133,17 @@ TriangleMesh make_cylinder(double r, double h, double fa)
     return mesh;
 }
 
-
-TriangleMesh make_cone(double r, double h, double fa)
+indexed_triangle_set its_make_cone(double r, double h, double fa)
 {
-    Pointf3s vertices;
-    std::vector<Vec3i>	facets;
-    vertices.reserve(3+size_t(2*PI/fa));
-    vertices.reserve(3+2*size_t(2*PI/fa));
+    indexed_triangle_set mesh;
+    auto& vertices = mesh.vertices;
+    auto& facets = mesh.indices;
+    vertices.reserve(3 + 2 * size_t(2 * PI / fa));
+
+    // base center and top vertex
+    vertices.emplace_back(Vec3f::Zero());
+    vertices.emplace_back(Vec3f(0., 0., h));
 
-    vertices = { Vec3d::Zero(), Vec3d(0., 0., h) }; // base center and top vertex
     size_t i = 0;
     for (double angle=0; angle<2*PI; angle+=fa) {
         vertices.emplace_back(r*std::cos(angle), r*std::sin(angle), 0.);
@@ -1150,11 +1156,15 @@ TriangleMesh make_cone(double r, double h, double fa)
     facets.emplace_back(0, 2, i+1); // close the shape
     facets.emplace_back(1, i+1, 2);
 
-    TriangleMesh mesh(std::move(vertices), std::move(facets));
-    mesh.repair();
     return mesh;
 }
 
+TriangleMesh make_cone(double radius, double fa)
+{
+    TriangleMesh mesh(its_make_cone(radius, fa));
+    mesh.repair();
+    return mesh;
+}
 
 // Generates mesh for a sphere centered about the origin, using the generated angle
 // to determine the granularity. 
@@ -1218,7 +1228,6 @@ TriangleMesh make_sphere(double radius, double fa)
 {
     TriangleMesh mesh(its_make_sphere(radius, fa));
     mesh.repair();
-
     return mesh;
 }
 
@@ -1335,4 +1344,13 @@ std::vector<Vec3i> its_face_neighbors_par(const indexed_triangle_set &its)
     return create_face_neighbors_index(ex_tbb, its);
 }
 
+std::vector<Vec3f> its_face_normals(const indexed_triangle_set &its) 
+{
+    std::vector<Vec3f> normals;
+    normals.reserve(its.indices.size());
+    for (stl_triangle_vertex_indices face : its.indices)
+        normals.push_back(its_face_normal(its, face));
+    return normals;
+}
+
 } // namespace Slic3r
diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp
index c463af5a2..60ab975c4 100644
--- a/src/libslic3r/TriangleMesh.hpp
+++ b/src/libslic3r/TriangleMesh.hpp
@@ -52,7 +52,6 @@ public:
     TriangleMeshPtrs split() const;
     void merge(const TriangleMesh &mesh);
     ExPolygons horizontal_projection() const;
-    const float* first_vertex() const { return this->stl.facet_start.empty() ? nullptr : &this->stl.facet_start.front().vertex[0](0); }
     // 2D convex hull of a 3D mesh projected into the Z=0 plane.
     Polygon convex_hull();
     BoundingBoxf3 bounding_box() const;
@@ -80,10 +79,14 @@ public:
     // Restore optional data possibly released by release_optional().
     void restore_optional();
 
-    stl_file stl;
+    const stl_stats& stats() const { return this->stl.stats; }
+    
     indexed_triangle_set its;
     bool repaired;
 
+//private:
+    stl_file stl;
+
 private:
     std::deque<uint32_t> find_unvisited_neighbors(std::vector<unsigned char> &facet_visited) const;
 };
@@ -205,16 +208,22 @@ void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B);
 void its_merge(indexed_triangle_set &A, const std::vector<Vec3f> &triangles);
 void its_merge(indexed_triangle_set &A, const Pointf3s &triangles);
 
-indexed_triangle_set its_make_cube(double x, double y, double z);
-TriangleMesh make_cube(double x, double y, double z);
+std::vector<Vec3f> its_face_normals(const indexed_triangle_set &its);
+inline Vec3f face_normal(const stl_vertex vertex[3]) { return  (vertex[1] - vertex[0]).cross(vertex[2] - vertex[1]).normalized(); }
+inline Vec3f face_normal_normalized(const stl_vertex vertex[3]) { return  face_normal(vertex).normalized(); }
+inline Vec3f its_face_normal(const indexed_triangle_set &its, const stl_triangle_vertex_indices face)
+    { const stl_vertex vertices[3] { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] }; return face_normal_normalized(vertices); }
+inline Vec3f its_face_normal(const indexed_triangle_set &its, const int face_idx)
+    { return its_face_normal(its, its.indices[face_idx]); }
 
-// Generate a TriangleMesh of a cylinder
-indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360));
-TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360));
-
-indexed_triangle_set its_make_sphere(double rho, double fa=(2*PI/360));
-TriangleMesh make_cone(double r, double h, double fa=(2*PI/360));
-TriangleMesh make_sphere(double rho, double fa=(2*PI/360));
+indexed_triangle_set    its_make_cube(double x, double y, double z);
+TriangleMesh            make_cube(double x, double y, double z);
+indexed_triangle_set    its_make_cylinder(double r, double h, double fa=(2*PI/360));
+TriangleMesh            make_cylinder(double r, double h, double fa=(2*PI/360));
+indexed_triangle_set    its_make_cone(double r, double h, double fa=(2*PI/360));
+TriangleMesh            make_cone(double r, double h, double fa=(2*PI/360));
+indexed_triangle_set    its_make_sphere(double radius, double fa);
+TriangleMesh            make_sphere(double rho, double fa=(2*PI/360));
 
 inline BoundingBoxf3 bounding_box(const TriangleMesh &m) { return m.bounding_box(); }
 inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its)
diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp
index 6178bcec8..987ef1c0a 100644
--- a/src/libslic3r/TriangleSelector.cpp
+++ b/src/libslic3r/TriangleSelector.cpp
@@ -9,16 +9,6 @@
 
 namespace Slic3r {
 
-static inline Vec3i root_neighbors(const TriangleMesh &mesh, int triangle_id)
-{
-    Vec3i neighbors;
-    const stl_neighbors& neighbors_src = mesh.stl.neighbors_start[triangle_id];
-    for (int i = 0; i < 3; ++i)
-        // Refuse a neighbor with a flipped normal.
-        neighbors(i) = neighbors_src.neighbor[i];
-    return neighbors;
-}
-
 #ifndef NDEBUG
 bool TriangleSelector::verify_triangle_midpoints(const Triangle &tr) const
 {
@@ -129,7 +119,7 @@ int TriangleSelector::select_unsplit_triangle(const Vec3f &hit, int facet_idx) c
     if (!m_triangles[facet_idx].valid())
         return -1;
 
-    Vec3i neighbors = root_neighbors(*m_mesh, facet_idx);
+    Vec3i neighbors = m_neighbors[facet_idx];
     assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
     return this->select_unsplit_triangle(hit, facet_idx, neighbors);
 }
@@ -167,7 +157,7 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
         if (! visited[facet]) {
             if (select_triangle(facet, new_state, triangle_splitting)) {
                 // add neighboring facets to list to be proccessed later
-                for (int neighbor_idx : m_mesh->stl.neighbors_start[facet].neighbor) {
+                for (int neighbor_idx : m_neighbors[facet]) {
                     if (neighbor_idx >=0 && (m_cursor.type == SPHERE || faces_camera(neighbor_idx)))
                         facets_to_check.push_back(neighbor_idx);
                 }
@@ -213,12 +203,12 @@ void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_st
 
             if (current_facet < m_orig_size_indices)
                 // Propagate over the original triangles.
-                for (int neighbor_idx : m_mesh->stl.neighbors_start[current_facet].neighbor) {
+                for (int neighbor_idx : m_neighbors[current_facet]) {
                     assert(neighbor_idx >= -1);
                     if (neighbor_idx >= 0 && !visited[neighbor_idx]) {
                         // Check if neighbour_facet_idx is satisfies angle in seed_fill_angle and append it to facet_queue if it do.
-                        const Vec3f &n1 = m_mesh->stl.facet_start[m_triangles[neighbor_idx].source_triangle].normal;
-                        const Vec3f &n2 = m_mesh->stl.facet_start[m_triangles[current_facet].source_triangle].normal;
+                        const Vec3f &n1 = m_face_normals[m_triangles[neighbor_idx].source_triangle];
+                        const Vec3f &n2 = m_face_normals[m_triangles[current_facet].source_triangle];
                         if (std::clamp(n1.dot(n2), 0.f, 1.f) >= facet_angle_limit)
                             facet_queue.push(neighbor_idx);
                     }
@@ -261,7 +251,7 @@ std::pair<std::vector<Vec3i>, std::vector<Vec3i>> TriangleSelector::precompute_a
     std::vector<Vec3i> neighbors(m_triangles.size(), Vec3i(-1, -1, -1));
     std::vector<Vec3i> neighbors_propagated(m_triangles.size(), Vec3i(-1, -1, -1));
     for (int facet_idx = 0; facet_idx < this->m_orig_size_indices; ++facet_idx) {
-        neighbors[facet_idx]            = root_neighbors(*m_mesh, facet_idx);
+        neighbors[facet_idx]            = m_neighbors[facet_idx];
         neighbors_propagated[facet_idx] = neighbors[facet_idx];
         assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors[facet_idx]));
         if (m_triangles[facet_idx].is_split())
@@ -403,7 +393,7 @@ bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type,
     if (! m_triangles[facet_idx].valid())
         return false;
 
-    Vec3i neighbors = root_neighbors(*m_mesh, facet_idx);
+    Vec3i neighbors = m_neighbors[facet_idx];
     assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
 
     if (! select_triangle_recursive(facet_idx, neighbors, type, triangle_splitting))
@@ -906,14 +896,10 @@ bool TriangleSelector::is_pointer_in_triangle(int facet_idx) const
 bool TriangleSelector::faces_camera(int facet) const
 {
     assert(facet < m_orig_size_indices);
-    // The normal is cached in mesh->stl, use it.
-    Vec3f normal = m_mesh->stl.facet_start[facet].normal;
-
-    if (! m_cursor.uniform_scaling) {
-        // Transform the normal into world coords.
-        normal = m_cursor.trafo_normal * normal;
-    }
-    return (normal.dot(m_cursor.dir) < 0.);
+    Vec3f n = m_face_normals[facet];
+    if (! m_cursor.uniform_scaling)
+        n = m_cursor.trafo_normal * n;
+    return n.dot(m_cursor.dir) < 0.;
 }
 
 
@@ -1094,7 +1080,7 @@ void TriangleSelector::garbage_collect()
 }
 
 TriangleSelector::TriangleSelector(const TriangleMesh& mesh)
-    : m_mesh{&mesh}
+    : m_mesh{mesh}, m_neighbors(its_face_neighbors(mesh.its)), m_face_normals(its_face_normals(mesh.its))
 {
     reset();
 }
@@ -1107,16 +1093,17 @@ void TriangleSelector::reset()
     m_invalid_triangles = 0;
     m_free_triangles_head = -1;
     m_free_vertices_head = -1;
-    m_vertices.reserve(m_mesh->its.vertices.size());
-    for (const stl_vertex& vert : m_mesh->its.vertices)
+    m_vertices.reserve(m_mesh.its.vertices.size());
+    for (const stl_vertex& vert : m_mesh.its.vertices)
         m_vertices.emplace_back(vert);
-    m_triangles.reserve(m_mesh->its.indices.size());
-    for (size_t i = 0; i < m_mesh->its.indices.size(); ++i) {
-        const stl_triangle_vertex_indices &ind = m_mesh->its.indices[i];
+    m_triangles.reserve(m_mesh.its.indices.size());
+    for (size_t i = 0; i < m_mesh.its.indices.size(); ++i) {
+        const stl_triangle_vertex_indices &ind = m_mesh.its.indices[i];
         push_triangle(ind[0], ind[1], ind[2], int(i));
     }
     m_orig_size_vertices = int(m_vertices.size());
     m_orig_size_indices  = int(m_triangles.size());
+
 }
 
 
@@ -1286,7 +1273,7 @@ indexed_triangle_set TriangleSelector::get_facets_strict(EnforcerBlockerType sta
         }
 
     for (int itriangle = 0; itriangle < m_orig_size_indices; ++ itriangle)
-        this->get_facets_strict_recursive(m_triangles[itriangle], root_neighbors(*m_mesh, itriangle), state, out.indices);
+        this->get_facets_strict_recursive(m_triangles[itriangle], m_neighbors[itriangle], state, out.indices);
 
     for (auto &triangle : out.indices)
         for (int i = 0; i < 3; ++ i)
@@ -1398,7 +1385,7 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i &vertices, const
 std::vector<Vec2i> TriangleSelector::get_seed_fill_contour() const {
     std::vector<Vec2i> edges_out;
     for (int facet_idx = 0; facet_idx < this->m_orig_size_indices; ++facet_idx) {
-        const Vec3i neighbors = root_neighbors(*m_mesh, facet_idx);
+        const Vec3i neighbors = m_neighbors[facet_idx];
         assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
         this->get_seed_fill_contour_recursive(facet_idx, neighbors, neighbors, edges_out);
     }
@@ -1522,10 +1509,10 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
 
     // Reserve number of triangles as if each triangle was saved with 4 bits.
     // With MMU painting this estimate may be somehow low, but better than nothing.
-    m_triangles.reserve(std::max(m_mesh->its.indices.size(), data.second.size() / 4));
+    m_triangles.reserve(std::max(m_mesh.its.indices.size(), data.second.size() / 4));
     // Number of triangles is twice the number of vertices on a large manifold mesh of genus zero.
     // Here the triangles count account for both the nodes and leaves, thus the following line may overestimate.
-    m_vertices.reserve(std::max(m_mesh->its.vertices.size(), m_triangles.size() / 2));
+    m_vertices.reserve(std::max(m_mesh.its.vertices.size(), m_triangles.size() / 2));
 
     // Vector to store all parents that have offsprings.
     struct ProcessingInfo {
@@ -1565,7 +1552,7 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
                 if (is_split) {
                     // root is split, add it into list of parents and split it.
                     // then go to the next.
-                    Vec3i neighbors = root_neighbors(*m_mesh, triangle_id);
+                    Vec3i neighbors = m_neighbors[triangle_id];
                     parents.push_back({triangle_id, neighbors, 0, num_of_children});
                     m_triangles[triangle_id].set_division(num_of_split_sides, special_side);
                     perform_split(triangle_id, neighbors, EnforcerBlockerType::NONE);
diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp
index 2ab053123..05a78e2ee 100644
--- a/src/libslic3r/TriangleSelector.hpp
+++ b/src/libslic3r/TriangleSelector.hpp
@@ -161,7 +161,9 @@ protected:
     // Lists of vertices and triangles, both original and new
     std::vector<Vertex> m_vertices;
     std::vector<Triangle> m_triangles;
-    const TriangleMesh* m_mesh;
+    const TriangleMesh &m_mesh;
+    const std::vector<Vec3i> m_neighbors;
+    const std::vector<Vec3f> m_face_normals;
 
     // Number of invalid triangles (to trigger garbage collection).
     int m_invalid_triangles;
diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp
index ba72c432d..f693143c4 100644
--- a/src/slic3r/GUI/3DScene.cpp
+++ b/src/slic3r/GUI/3DScene.cpp
@@ -9,9 +9,7 @@
 #include "3DScene.hpp"
 #include "GLShader.hpp"
 #include "GUI_App.hpp"
-#if ENABLE_ENVIRONMENT_MAP || ENABLE_SINKING_CONTOURS
 #include "Plater.hpp"
-#endif // ENABLE_ENVIRONMENT_MAP || ENABLE_SINKING_CONTOURS
 
 #include "libslic3r/ExtrusionEntity.hpp"
 #include "libslic3r/ExtrusionEntityCollection.hpp"
@@ -25,9 +23,7 @@
 #include "libslic3r/AppConfig.hpp"
 #include "libslic3r/PresetBundle.hpp"
 #include "libslic3r/ClipperUtils.hpp"
-#if ENABLE_SINKING_CONTOURS
 #include "libslic3r/Tesselate.hpp"
-#endif // ENABLE_SINKING_CONTOURS
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -158,22 +154,27 @@ void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh& mesh)
     }
     else {
 #endif // ENABLE_SMOOTH_NORMALS
-        this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count());
-
-        unsigned int vertices_count = 0;
-        for (int i = 0; i < (int)mesh.stl.stats.number_of_facets; ++i) {
-            const stl_facet& facet = mesh.stl.facet_start[i];
-            for (int j = 0; j < 3; ++j)
-                this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2));
-
-            this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2);
-            vertices_count += 3;
-        }
+        this->load_its_flat_shading(mesh.its);
 #if ENABLE_SMOOTH_NORMALS
     }
 #endif // ENABLE_SMOOTH_NORMALS
 }
 
+void GLIndexedVertexArray::load_its_flat_shading(const indexed_triangle_set &its)
+{
+    this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * its.indices.size());
+    unsigned int vertices_count = 0;
+    for (int i = 0; i < int(its.indices.size()); ++ i) {
+        stl_triangle_vertex_indices face        = its.indices[i];
+        stl_vertex                  vertex[3]   = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] };
+        stl_vertex                  n           = face_normal_normalized(vertex);
+        for (int j = 0; j < 3; ++j)
+            this->push_geometry(vertex[j](0), vertex[j](1), vertex[j](2), n(0), n(1), n(2));
+        this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2);
+        vertices_count += 3;
+    }
+}
+
 void GLIndexedVertexArray::finalize_geometry(bool opengl_initialized)
 {
     assert(this->vertices_and_normals_interleaved_VBO_id == 0);
@@ -288,7 +289,6 @@ void GLIndexedVertexArray::render(
     glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
 }
 
-#if ENABLE_SINKING_CONTOURS
 const float GLVolume::SinkingContours::HalfWidth = 0.25f;
 
 void GLVolume::SinkingContours::render()
@@ -357,7 +357,6 @@ void GLVolume::SinkingContours::update()
     else
         m_model.reset();
 }
-#endif // ENABLE_SINKING_CONTOURS
 
 const std::array<float, 4> GLVolume::SELECTED_COLOR = { 0.0f, 1.0f, 0.0f, 1.0f };
 const std::array<float, 4> GLVolume::HOVER_SELECT_COLOR = { 0.4f, 0.9f, 0.1f, 1.0f };
@@ -376,12 +375,8 @@ const std::array<std::array<float, 4>, 4> GLVolume::MODEL_COLOR = { {
 } };
 
 GLVolume::GLVolume(float r, float g, float b, float a)
-    : m_transformed_bounding_box_dirty(true)
-    , m_sla_shift_z(0.0)
-    , m_transformed_convex_hull_bounding_box_dirty(true)
-#if ENABLE_SINKING_CONTOURS
+    : m_sla_shift_z(0.0)
     , m_sinking_contours(*this)
-#endif // ENABLE_SINKING_CONTOURS
     // geometry_id == 0 -> invalid
     , geometry_id(std::pair<size_t, size_t>(0, 0))
     , extruder_id(0)
@@ -399,9 +394,7 @@ GLVolume::GLVolume(float r, float g, float b, float a)
     , force_transparent(false)
     , force_native_color(false)
     , force_neutral_color(false)
-#if ENABLE_SINKING_CONTOURS
     , force_sinking_contours(false)
-#endif // ENABLE_SINKING_CONTOURS
     , tverts_range(0, size_t(-1))
     , qverts_range(0, size_t(-1))
 {
@@ -506,32 +499,27 @@ bool GLVolume::is_left_handed() const
 
 const BoundingBoxf3& GLVolume::transformed_bounding_box() const
 {
-    const BoundingBoxf3& box = bounding_box();
-    assert(box.defined || box.min(0) >= box.max(0) || box.min(1) >= box.max(1) || box.min(2) >= box.max(2));
-
-    BoundingBoxf3* transformed_bounding_box = const_cast<BoundingBoxf3*>(&m_transformed_bounding_box);
-    bool* transformed_bounding_box_dirty = const_cast<bool*>(&m_transformed_bounding_box_dirty);
-    if (*transformed_bounding_box_dirty) {
-        *transformed_bounding_box = box.transformed(world_matrix());
-        *transformed_bounding_box_dirty = false;
+    if (!m_transformed_bounding_box.has_value()) {
+        const BoundingBoxf3& box = bounding_box();
+        assert(box.defined || box.min.x() >= box.max.x() || box.min.y() >= box.max.y() || box.min.z() >= box.max.z());
+        std::optional<BoundingBoxf3>* trans_box = const_cast<std::optional<BoundingBoxf3>*>(&m_transformed_bounding_box);
+        *trans_box = box.transformed(world_matrix());
     }
-    return *transformed_bounding_box;
+    return *m_transformed_bounding_box;
 }
 
 const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const
 {
-    BoundingBoxf3* transformed_convex_hull_bounding_box = const_cast<BoundingBoxf3*>(&m_transformed_convex_hull_bounding_box);
-    bool* transformed_convex_hull_bounding_box_dirty = const_cast<bool*>(&m_transformed_convex_hull_bounding_box_dirty);
-    if (*transformed_convex_hull_bounding_box_dirty) {
-        *transformed_convex_hull_bounding_box = this->transformed_convex_hull_bounding_box(world_matrix());
-        *transformed_convex_hull_bounding_box_dirty = false;
+    if (!m_transformed_convex_hull_bounding_box.has_value()) {
+        std::optional<BoundingBoxf3>* trans_box = const_cast<std::optional<BoundingBoxf3>*>(&m_transformed_convex_hull_bounding_box);
+        *trans_box = transformed_convex_hull_bounding_box(world_matrix());
     }
-    return *transformed_convex_hull_bounding_box;
+    return *m_transformed_convex_hull_bounding_box;
 }
 
 BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const
 {
-	return (m_convex_hull && m_convex_hull->stl.stats.number_of_facets > 0) ? 
+	return (m_convex_hull && m_convex_hull->facets_count() > 0) ?
 		m_convex_hull->transformed_bounding_box(trafo) :
         bounding_box().transformed(trafo);
 }
@@ -605,12 +593,10 @@ bool GLVolume::is_below_printbed() const
     return transformed_convex_hull_bounding_box().max.z() < 0.0;
 }
 
-#if ENABLE_SINKING_CONTOURS
 void GLVolume::render_sinking_contours()
 {
     m_sinking_contours.render();
 }
-#endif // ENABLE_SINKING_CONTOURS
 
 std::vector<int> GLVolumeCollection::load_object(
     const ModelObject       *model_object,
@@ -767,10 +753,11 @@ int GLVolumeCollection::load_wipe_tower_preview(
     TriangleMesh brim_mesh = make_cube(width + 2.f * brim_width, depth + 2.f * brim_width, 0.2f);
     brim_mesh.translate(-brim_width, -brim_width, 0.f);
     mesh.merge(brim_mesh);
+    mesh.repair();
 
     volumes.emplace_back(new GLVolume(color));
     GLVolume& v = *volumes.back();
-    v.indexed_vertex_array.load_mesh(mesh);
+    v.indexed_vertex_array.load_mesh(mesh);    
     v.indexed_vertex_array.finalize_geometry(opengl_initialized);
     v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
     v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
@@ -834,11 +821,9 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo
 
 void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func) const
 {
-#if ENABLE_SINKING_CONTOURS
     GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func);
     if (to_render.empty())
         return;
-#endif // ENABLE_SINKING_CONTOURS
 
     GLShaderProgram* shader = GUI::wxGetApp().get_current_shader();
     if (shader == nullptr)
@@ -853,7 +838,6 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
     if (disable_cullface)
         glsafe(::glDisable(GL_CULL_FACE));
 
-#if ENABLE_SINKING_CONTOURS
     for (GLVolumeWithIdAndZ& volume : to_render) {
         volume.first->set_render_color();
 
@@ -903,7 +887,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
         glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
     }
 
-    if (m_show_sinking_contours)
+    if (m_show_sinking_contours) {
         for (GLVolumeWithIdAndZ& volume : to_render) {
             // render sinking contours of hovered/displaced volumes
             if (volume.first->is_sinking() && !volume.first->is_below_printbed() &&
@@ -915,48 +899,8 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
                 shader->start_using();
             }
         }
-#else
-    glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
-    glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
- 
-    shader->set_uniform("print_box.min", m_print_box_min, 3);
-    shader->set_uniform("print_box.max", m_print_box_max, 3);
-    shader->set_uniform("z_range", m_z_range, 2);
-    shader->set_uniform("clipping_plane", m_clipping_plane, 4);
-    shader->set_uniform("slope.normal_z", m_slope.normal_z);
-
-#if ENABLE_ENVIRONMENT_MAP
-    unsigned int environment_texture_id = GUI::wxGetApp().plater()->get_environment_texture_id();
-    bool use_environment_texture = environment_texture_id > 0 && GUI::wxGetApp().app_config->get("use_environment_map") == "1";
-    shader->set_uniform("use_environment_tex", use_environment_texture);
-    if (use_environment_texture)
-        glsafe(::glBindTexture(GL_TEXTURE_2D, environment_texture_id));
-#endif // ENABLE_ENVIRONMENT_MAP
-    glcheck();
-
-    GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
-    for (GLVolumeWithIdAndZ& volume : to_render) {
-        volume.first->set_render_color();
-        shader->set_uniform("uniform_color", volume.first->render_color);
-        shader->set_uniform("print_box.actived", volume.first->shader_outside_printer_detection_enabled);
-        shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix());
-        shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower);
-        shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
-        volume.first->render();
     }
 
-#if ENABLE_ENVIRONMENT_MAP
-    if (use_environment_texture)
-        glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
-#endif // ENABLE_ENVIRONMENT_MAP
-
-    glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
-    glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
-
-    glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
-    glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
-#endif // ENABLE_SINKING_CONTOURS
-
     if (disable_cullface)
         glsafe(::glEnable(GL_CULL_FACE));
 
diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp
index 1a85cc41e..78b9a96d9 100644
--- a/src/slic3r/GUI/3DScene.hpp
+++ b/src/slic3r/GUI/3DScene.hpp
@@ -8,11 +8,10 @@
 #include "libslic3r/Utils.hpp"
 #include "libslic3r/Geometry.hpp"
 
-#if ENABLE_SINKING_CONTOURS
 #include "GLModel.hpp"
-#endif // ENABLE_SINKING_CONTOURS
 
 #include <functional>
+#include <optional>
 
 #define HAS_GLSAFE
 #ifdef HAS_GLSAFE
@@ -128,6 +127,8 @@ public:
     void load_mesh(const TriangleMesh& mesh) { this->load_mesh_full_shading(mesh); }
 #endif // ENABLE_SMOOTH_NORMALS
 
+    void load_its_flat_shading(const indexed_triangle_set &its);
+
     inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; }
 
     inline void reserve(size_t sz) {
@@ -258,9 +259,7 @@ public:
     enum EHoverState : unsigned char
     {
         HS_None,
-#if ENABLE_SINKING_CONTOURS
         HS_Hover,
-#endif // ENABLE_SINKING_CONTOURS
         HS_Select,
         HS_Deselect
     };
@@ -275,17 +274,12 @@ private:
     // Shift in z required by sla supports+pad
     double        m_sla_shift_z;
     // Bounding box of this volume, in unscaled coordinates.
-    BoundingBoxf3 m_transformed_bounding_box;
-    // Whether or not is needed to recalculate the transformed bounding box.
-    bool          m_transformed_bounding_box_dirty;
+    std::optional<BoundingBoxf3> m_transformed_bounding_box;
     // Convex hull of the volume, if any.
     std::shared_ptr<const TriangleMesh> m_convex_hull;
     // Bounding box of this volume, in unscaled coordinates.
-    BoundingBoxf3 m_transformed_convex_hull_bounding_box;
-    // Whether or not is needed to recalculate the transformed convex hull bounding box.
-    bool          m_transformed_convex_hull_bounding_box_dirty;
+    std::optional<BoundingBoxf3> m_transformed_convex_hull_bounding_box;
 
-#if ENABLE_SINKING_CONTOURS
     class SinkingContours
     {
         static const float HalfWidth;
@@ -303,7 +297,6 @@ private:
     };
 
     SinkingContours m_sinking_contours;
-#endif // ENABLE_SINKING_CONTOURS
 
 public:
     // Color of the triangles / quads held by this volume.
@@ -365,10 +358,8 @@ public:
 	    bool                force_native_color : 1;
         // Whether or not render this volume in neutral
         bool                force_neutral_color : 1;
-#if ENABLE_SINKING_CONTOURS
         // Whether or not to force rendering of sinking contours
         bool                force_sinking_contours : 1;
-#endif // ENABLE_SINKING_CONTOURS
     };
 
     // Is mouse or rectangle selection over this object to select/deselect it ?
@@ -490,16 +481,14 @@ public:
     void                finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); }
     void                release_geometry() { this->indexed_vertex_array.release_geometry(); }
 
-    void                set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; }
+    void                set_bounding_boxes_as_dirty() { m_transformed_bounding_box.reset(); m_transformed_convex_hull_bounding_box.reset(); }
 
     bool                is_sla_support() const;
     bool                is_sla_pad() const;
 
     bool                is_sinking() const;
     bool                is_below_printbed() const;
-#if ENABLE_SINKING_CONTOURS
     void                render_sinking_contours();
-#endif // ENABLE_SINKING_CONTOURS
 
     // Return an estimate of the memory consumed by this class.
     size_t 				cpu_memory_used() const { 
diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp
index 7d7ed08a2..f83078261 100644
--- a/src/slic3r/GUI/DoubleSlider.cpp
+++ b/src/slic3r/GUI/DoubleSlider.cpp
@@ -1556,10 +1556,8 @@ void Control::OnMotion(wxMouseEvent& event)
     event.Skip();
 
     // Set tooltips with information for each icon
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     if (GUI::wxGetApp().is_editor())
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
-    this->SetToolTip(get_tooltip(tick));
+        this->SetToolTip(get_tooltip(tick));
 
     if (action) {
         wxCommandEvent e(wxEVT_SCROLL_CHANGED);
diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index 06cfd0a1c..7dbb9d787 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -977,7 +977,7 @@ void Choice::BUILD() {
                 propagate_value();
         } );
 
-        temp->Bind(wxEVT_TEXT_ENTER, [this, temp](wxEvent& e) {
+        temp->Bind(wxEVT_TEXT_ENTER, [this](wxEvent& e) {
             EnterPressed enter(this);
             propagate_value();
         } );
diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp
index 742a11459..5456acd64 100644
--- a/src/slic3r/GUI/GCodeViewer.cpp
+++ b/src/slic3r/GUI/GCodeViewer.cpp
@@ -583,10 +583,8 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print&
         // Stealing out lines_ends should be safe because this gcode_result is processed only once (see the 1st if in this function).
         std::move(const_cast<std::vector<size_t>&>(gcode_result.lines_ends)));
 
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     if (wxGetApp().is_gcode_viewer())
         m_custom_gcode_per_print_z = gcode_result.custom_gcode_per_print_z;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
 
     load_toolpaths(gcode_result);
 
@@ -744,9 +742,7 @@ void GCodeViewer::reset()
     m_layers_z_range = { 0, 0 };
     m_roles = std::vector<ExtrusionRole>();
     m_print_statistics.reset();
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     m_custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     m_sequential_view.gcode_window.reset();
 #if ENABLE_GCODE_VIEWER_STATISTICS
     m_statistics.reset_all();
@@ -3048,7 +3044,11 @@ void GCodeViewer::render_legend(float& legend_height)
 
     bool imperial_units = wxGetApp().app_config->get("use_inches") == "1";
 
+#if ENABLE_SEAMS_USING_BATCHED_MODELS
     auto append_item = [icon_size, percent_bar_size, &imgui, imperial_units](EItemType type, const Color& color, const std::string& label,
+#else
+    auto append_item = [this, icon_size, percent_bar_size, &imgui, imperial_units](EItemType type, const Color& color, const std::string& label,
+#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
         bool visible = true, const std::string& time = "", float percent = 0.0f, float max_percent = 0.0f, const std::array<float, 4>& offsets = { 0.0f, 0.0f, 0.0f, 0.0f },
         double used_filament_m = 0.0, double used_filament_g = 0.0,
         std::function<void()> callback = nullptr) {
@@ -3446,11 +3446,7 @@ void GCodeViewer::render_legend(float& legend_height)
     }
     case EViewType::ColorPrint:
     {
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
         const std::vector<CustomGCode::Item>& custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes : m_custom_gcode_per_print_z;
-#else
-        const std::vector<CustomGCode::Item>& custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
         size_t total_items = 1;
         for (unsigned char i : m_extruder_ids) {
             total_items += color_print_ranges(i, custom_gcode_per_print_z).size();
@@ -3548,11 +3544,7 @@ void GCodeViewer::render_legend(float& legend_height)
         auto generate_partial_times = [this, get_used_filament_from_volume](const TimesList& times, const std::vector<double>& used_filaments) {
             PartialTimes items;
 
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
             std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes : m_custom_gcode_per_print_z;
-#else
-            std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
             int extruders_count = wxGetApp().extruders_edited_cnt();
             std::vector<Color> last_color(extruders_count);
             for (int i = 0; i < extruders_count; ++i) {
diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp
index f566c80bd..0d5552d67 100644
--- a/src/slic3r/GUI/GCodeViewer.hpp
+++ b/src/slic3r/GUI/GCodeViewer.hpp
@@ -783,9 +783,7 @@ private:
     GCodeProcessor::Result::SettingsIds m_settings_ids;
     std::array<SequentialRangeCap, 2> m_sequential_range_caps;
 
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     std::vector<CustomGCode::Item> m_custom_gcode_per_print_z;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
 
 public:
     GCodeViewer();
@@ -834,10 +832,8 @@ public:
 
     void toggle_gcode_window_visibility() { m_sequential_view.gcode_window.toggle_visibility(); }
 
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     std::vector<CustomGCode::Item>& get_custom_gcode_per_print_z() { return m_custom_gcode_per_print_z; }
     size_t get_extruders_count() { return m_extruders_count; }
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
 
 private:
     void load_toolpaths(const GCodeProcessor::Result& gcode_result);
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 551760ee9..83b907f01 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1450,19 +1450,13 @@ void GLCanvas3D::render()
     glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
     _render_background();
 
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
     _render_objects(GLVolumeCollection::ERenderType::Opaque);
-#else
-    _render_objects();
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
     if (!m_main_toolbar.is_enabled())
         _render_gcode();
     _render_sla_slices();
     _render_selection();
     _render_bed(!camera.is_looking_downward(), true);
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
     _render_objects(GLVolumeCollection::ERenderType::Transparent);
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
 
     _render_sequential_clearance();
 #if ENABLE_RENDER_SELECTION_CENTER
@@ -2932,7 +2926,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
         return;
     }
 
-#if ENABLE_SINKING_CONTOURS
     for (GLVolume* volume : m_volumes.volumes) {
         volume->force_sinking_contours = false;
     }
@@ -2944,7 +2937,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
         }
         m_dirty = true;
     };
-#endif // ENABLE_SINKING_CONTOURS
 
     if (m_gizmos.on_mouse(evt)) {
         if (wxWindow::FindFocus() != m_canvas)
@@ -2970,7 +2962,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
             default: { break; }
             }
         }
-#if ENABLE_SINKING_CONTOURS
         else if (evt.Dragging()) {
             switch (m_gizmos.get_current_type())
             {
@@ -2984,7 +2975,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
             default: { break; }
             }
         }
-#endif // ENABLE_SINKING_CONTOURS
 
         return;
     }
@@ -3294,10 +3284,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
     else
         evt.Skip();
 
-#if ENABLE_SINKING_CONTOURS
     if (m_moving)
         show_sinking_contours();
-#endif // ENABLE_SINKING_CONTOURS
 
 #ifdef __WXMSW__
 	if (on_enter_workaround)
@@ -5071,12 +5059,7 @@ void GLCanvas3D::_render_bed_for_picking(bool bottom)
     wxGetApp().plater()->get_bed().render_for_picking(*this, bottom, scale_factor);
 }
 
-
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
 void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
-#else
-void GLCanvas3D::_render_objects()
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
 {
     if (m_volumes.empty())
         return;
@@ -5108,20 +5091,14 @@ void GLCanvas3D::_render_objects()
     if (shader != nullptr) {
         shader->start_using();
 
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
         switch (type)
         {
         default:
         case GLVolumeCollection::ERenderType::Opaque:
         {
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
             if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) {
                 int object_id = m_layers_editing.last_object_id;
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
                 m_volumes.render(type, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) {
-#else
-                m_volumes.render(GLVolumeCollection::ERenderType::Opaque, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) {
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
                     // Which volume to paint without the layer height profile shader?
                     return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
                     });
@@ -5130,11 +5107,7 @@ void GLCanvas3D::_render_objects()
             }
             else {
                 // do not cull backfaces to show broken geometry, if any
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
                 m_volumes.render(type, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this](const GLVolume& volume) {
-#else
-                m_volumes.render(GLVolumeCollection::ERenderType::Opaque, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this](const GLVolume& volume) {
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
                     return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
                     });
             }
@@ -5153,7 +5126,6 @@ void GLCanvas3D::_render_objects()
                     shader->start_using();
                 }
             }
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
             break;
         }
         case GLVolumeCollection::ERenderType::Transparent:
@@ -5162,9 +5134,6 @@ void GLCanvas3D::_render_objects()
             break;
         }
         }
-#else
-        m_volumes.render(GLVolumeCollection::ERenderType::Transparent, false, wxGetApp().plater()->get_camera().get_view_matrix());
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
         shader->stop_using();
     }
 
@@ -5673,11 +5642,8 @@ void GLCanvas3D::_update_volumes_hover_state()
                 }
             }
         }
-#if ENABLE_SINKING_CONTOURS
         else if (volume.selected)
             volume.hover = GLVolume::HS_Hover;
-#endif // ENABLE_SINKING_CONTOURS
-
     }
 }
 
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 704adb010..e03c4a71d 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -710,10 +710,8 @@ public:
     void set_toolpath_view_type(GCodeViewer::EViewType type);
     void set_volumes_z_range(const std::array<double, 2>& range);
     void set_toolpaths_z_range(const std::array<unsigned int, 2>& range);
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     std::vector<CustomGCode::Item>& get_custom_gcode_per_print_z() { return m_gcode_viewer.get_custom_gcode_per_print_z(); }
     size_t get_gcode_extruders_count() { return m_gcode_viewer.get_extruders_count(); }
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
 
     std::vector<int> load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs);
     std::vector<int> load_object(const Model& model, int obj_idx);
@@ -895,11 +893,7 @@ private:
     void _render_background() const;
     void _render_bed(bool bottom, bool show_axes);
     void _render_bed_for_picking(bool bottom);
-#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
     void _render_objects(GLVolumeCollection::ERenderType type);
-#else
-    void _render_objects();
-#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
     void _render_gcode();
     void _render_selection() const;
     void _render_sequential_clearance();
diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp
index d22925673..298fb21b0 100644
--- a/src/slic3r/GUI/GLModel.cpp
+++ b/src/slic3r/GUI/GLModel.cpp
@@ -7,9 +7,7 @@
 
 #include "libslic3r/TriangleMesh.hpp"
 #include "libslic3r/Model.hpp"
-#if ENABLE_SINKING_CONTOURS
 #include "libslic3r/Polygon.hpp"
-#endif // ENABLE_SINKING_CONTOURS
 
 #include <boost/filesystem/operations.hpp>
 #include <boost/algorithm/string/predicate.hpp>
@@ -78,7 +76,7 @@ void GLModel::init_from(const InitializationData& data)
     }
 }
 
-void GLModel::init_from(const TriangleMesh& mesh)
+void GLModel::init_from(const indexed_triangle_set& its, const BoundingBoxf3 &bbox)
 {
     if (!m_render_data.empty()) // call reset() if you want to reuse this model
         return;
@@ -86,31 +84,36 @@ void GLModel::init_from(const TriangleMesh& mesh)
     RenderData data;
     data.type = PrimitiveType::Triangles;
 
-    std::vector<float> vertices = std::vector<float>(18 * mesh.stl.stats.number_of_facets);
-    std::vector<unsigned int> indices = std::vector<unsigned int>(3 * mesh.stl.stats.number_of_facets);
+    std::vector<float> vertices = std::vector<float>(18 * its.indices.size());
+    std::vector<unsigned int> indices = std::vector<unsigned int>(3 * its.indices.size());
 
     unsigned int vertices_count = 0;
-    for (uint32_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) {
-        const stl_facet& facet = mesh.stl.facet_start[i];
-        for (size_t j = 0; j < 3; ++j) {
+    for (uint32_t i = 0; i < its.indices.size(); ++i) {
+        stl_triangle_vertex_indices face      = its.indices[i];
+        stl_vertex                  vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] };
+        stl_vertex                  n         = face_normal_normalized(vertex);
+        for (size_t j = 0; j < 3; ++ j) {
             size_t offset = i * 18 + j * 6;
-            ::memcpy(static_cast<void*>(&vertices[offset]), static_cast<const void*>(facet.vertex[j].data()), 3 * sizeof(float));
-            ::memcpy(static_cast<void*>(&vertices[3 + offset]), static_cast<const void*>(facet.normal.data()), 3 * sizeof(float));
+            ::memcpy(static_cast<void*>(&vertices[offset]), static_cast<const void*>(vertex[j].data()), 3 * sizeof(float));
+            ::memcpy(static_cast<void*>(&vertices[3 + offset]), static_cast<const void*>(n.data()), 3 * sizeof(float));
         }
-        for (size_t j = 0; j < 3; ++j) {
+        for (size_t j = 0; j < 3; ++j)
             indices[i * 3 + j] = vertices_count + j;
-        }
         vertices_count += 3;
     }
 
     data.indices_count = static_cast<unsigned int>(indices.size());
-    m_bounding_box = mesh.bounding_box();
+    m_bounding_box = bbox;
 
     send_to_gpu(data, vertices, indices);
     m_render_data.emplace_back(data);
 }
 
-#if ENABLE_SINKING_CONTOURS
+void GLModel::init_from(const indexed_triangle_set& its)
+{
+    this->init_from(its, bounding_box(its));
+}
+
 void GLModel::init_from(const Polygons& polygons, float z)
 {
     auto append_polygon = [](const Polygon& polygon, float z, GUI::GLModel::InitializationData& data) {
@@ -137,7 +140,6 @@ void GLModel::init_from(const Polygons& polygons, float z)
     }
     init_from(init_data);
 }
-#endif // ENABLE_SINKING_CONTOURS
 
 bool GLModel::init_from_file(const std::string& filename)
 {
@@ -157,7 +159,9 @@ bool GLModel::init_from_file(const std::string& filename)
         return false;
     }
 
-    init_from(model.mesh());
+    TriangleMesh mesh = model.mesh();
+    mesh.require_shared_vertices();
+    init_from(mesh.its, mesh.bounding_box());
 
     m_filename = filename;
 
diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp
index 7422466f0..f2fc9da9b 100644
--- a/src/slic3r/GUI/GLModel.hpp
+++ b/src/slic3r/GUI/GLModel.hpp
@@ -6,13 +6,13 @@
 #include <vector>
 #include <string>
 
+struct indexed_triangle_set;
+
 namespace Slic3r {
 
 class TriangleMesh;
-#if ENABLE_SINKING_CONTOURS
 class Polygon;
 using Polygons = std::vector<Polygon>;
-#endif // ENABLE_SINKING_CONTOURS
 
 namespace GUI {
 
@@ -70,10 +70,9 @@ namespace GUI {
         virtual ~GLModel() { reset(); }
 
         void init_from(const InitializationData& data);
-        void init_from(const TriangleMesh& mesh);
-#if ENABLE_SINKING_CONTOURS
+        void init_from(const indexed_triangle_set& its, const BoundingBoxf3& bbox);
+        void init_from(const indexed_triangle_set& its);
         void init_from(const Polygons& polygons, float z);
-#endif // ENABLE_SINKING_CONTOURS
         bool init_from_file(const std::string& filename);
 
         // if entity_id == -1 set the color of all entities
diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp
index 515b6ed3a..cea68eaaa 100644
--- a/src/slic3r/GUI/GLToolbar.cpp
+++ b/src/slic3r/GUI/GLToolbar.cpp
@@ -1175,9 +1175,6 @@ void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighte
     float right = left + scaled_icons_size;
 
     unsigned int tex_id = m_arrow_texture.texture.get_id();
-    // width and height of icon arrow is pointing to
-    float tex_width = (float)m_icons_texture.get_width();
-    float tex_height = (float)m_icons_texture.get_height();
     // arrow width and height
     float arr_tex_width = (float)m_arrow_texture.texture.get_width();
     float arr_tex_height = (float)m_arrow_texture.texture.get_height();
diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp
index 00490d03d..023ba6f35 100644
--- a/src/slic3r/GUI/GUI.cpp
+++ b/src/slic3r/GUI/GUI.cpp
@@ -383,7 +383,9 @@ void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, cons
 
 		// the following line messes up the popup size the first time it is shown on wxWidgets 3.1.3
 //		comboCtrl->EnablePopupAnimation(false);
+#ifdef _WIN32
 		popup->SetFont(comboCtrl->GetFont());
+#endif // _WIN32
 		comboCtrl->SetPopupControl(popup);
 		wxString title = from_u8(text);
 		max_width = std::max(max_width, 60 + comboCtrl->GetTextExtent(title).x);
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index 79186a8ea..2c9aabc38 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -429,9 +429,8 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
         /* FT_OBJ */     "OBJ files (*.obj)|*.obj;*.OBJ",
         /* FT_AMF */     "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML",
         /* FT_3MF */     "3MF files (*.3mf)|*.3mf;*.3MF;",
-        /* FT_PRUSA */   "Prusa Control files (*.prusa)|*.prusa;*.PRUSA",
         /* FT_GCODE */   "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC",
-        /* FT_MODEL */   "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA",
+        /* FT_MODEL */   "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF",
         /* FT_PROJECT */ "Project files (*.3mf, *.amf)|*.3mf;*.3MF;*.amf;*.AMF",
         /* FT_GALLERY */ "Known files (*.stl, *.obj)|*.stl;*.STL;*.obj;*.OBJ",
 
diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp
index 5acab0c94..2af4a3a4a 100644
--- a/src/slic3r/GUI/GUI_App.hpp
+++ b/src/slic3r/GUI/GUI_App.hpp
@@ -55,7 +55,6 @@ enum FileType
     FT_OBJ,
     FT_AMF,
     FT_3MF,
-    FT_PRUSA,
     FT_GCODE,
     FT_MODEL,
     FT_PROJECT,
diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp
index 4fec12d14..3671ee130 100644
--- a/src/slic3r/GUI/GUI_Factories.cpp
+++ b/src/slic3r/GUI/GUI_Factories.cpp
@@ -689,7 +689,7 @@ wxMenuItem* MenuFactory::append_menu_item_fix_through_netfabb(wxMenu* menu)
         return nullptr;
     wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Fix through the Netfabb"), "",
         [](wxCommandEvent&) { obj_list()->fix_through_netfabb(); }, "", menu,
-        []() {return plater()->can_fix_through_netfabb(); }, plater());
+        []() {return plater()->can_fix_through_netfabb(); }, m_parent);
 
     return menu_item;
 }
@@ -698,7 +698,7 @@ wxMenuItem* MenuFactory::append_menu_item_simplify(wxMenu* menu)
 {
     wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Simplify model"), "",
         [](wxCommandEvent&) { obj_list()->simplify(); }, "", menu,
-        []() {return plater()->can_simplify(); }, plater());
+        []() {return plater()->can_simplify(); }, m_parent);
     menu->AppendSeparator();
 
     return menu_item;
@@ -779,7 +779,7 @@ void MenuFactory::append_menu_item_change_extruder(wxMenu* menu)
     }
 
     append_submenu(menu, extruder_selection_menu, wxID_ANY, name, _L("Use another extruder"),
-        "edit_uni"/* : "change_extruder"*/, []() {return true; }, GUI::wxGetApp().plater());
+        "edit_uni"/* : "change_extruder"*/, []() {return true; }, m_parent);
 
 //    menu->AppendSubMenu(extruder_selection_menu, name);
 }
@@ -1061,6 +1061,7 @@ wxMenu* MenuFactory::multi_selection_menu()
 
     wxMenu* menu = new MenuWithSeparators();
 
+    append_menu_item_fix_through_netfabb(menu);
     append_menu_item_reload_from_disk(menu);
     append_menu_items_convert_unit(menu);
     if (obj_list()->can_merge_to_multipart_object())
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index 378f91cbc..da4a842d4 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -19,6 +19,7 @@
 #include "Selection.hpp"
 #include "format.hpp"
 #include "NotificationManager.hpp"
+#include "MsgDialog.hpp"
 
 #include <boost/algorithm/string.hpp>
 #include <wx/progdlg.h>
@@ -369,7 +370,7 @@ void ObjectList::get_selection_indexes(std::vector<int>& obj_idxs, std::vector<i
         }
     }
 
-    std::sort(obj_idxs.begin(), obj_idxs.end(), std::greater<int>());
+    std::sort(obj_idxs.begin(), obj_idxs.end(), std::less<int>());
     obj_idxs.erase(std::unique(obj_idxs.begin(), obj_idxs.end()), obj_idxs.end());
 }
 
@@ -393,7 +394,7 @@ wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx /
 
     const stl_stats& stats = vol_idx == -1 ?
                             (*m_objects)[obj_idx]->get_object_stl_stats() :
-                            (*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stl.stats;
+                            (*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stats();
 
     if (stats.degenerate_facets > 0)
         tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d degenerate facet", "%1$d degenerate facets", stats.degenerate_facets), stats.degenerate_facets) + "\n";
@@ -4013,13 +4014,124 @@ void ObjectList::rename_item()
 
 void ObjectList::fix_through_netfabb() 
 {
-    int obj_idx, vol_idx;
-    get_selected_item_indexes(obj_idx, vol_idx);
+    // Do not fix anything when a gizmo is open. There might be issues with updates
+    // and what is worse, the snapshot time would refer to the internal stack.
+    if (!wxGetApp().plater()->canvas3D()->get_gizmos_manager().check_gizmos_closed_except(GLGizmosManager::Undefined))
+        return;
 
-    wxGetApp().plater()->fix_through_netfabb(obj_idx, vol_idx);
-    
-    update_item_error_icon(obj_idx, vol_idx);
-    update_info_items(obj_idx);
+    //          model_name
+    std::vector<std::string>                           succes_models;
+    //                   model_name     failing reason
+    std::vector<std::pair<std::string, std::string>>   failed_models;
+
+    std::vector<int> obj_idxs, vol_idxs;
+    get_selection_indexes(obj_idxs, vol_idxs);
+
+    std::vector<std::string> model_names;
+
+    // clear selections from the non-broken models if any exists
+    // and than fill names of models to repairing 
+    if (vol_idxs.empty()) {
+        for (int i = int(obj_idxs.size())-1; i >= 0; --i)
+                if (object(obj_idxs[i])->get_mesh_errors_count() == 0)
+                    obj_idxs.erase(obj_idxs.begin()+i);
+        for (int obj_idx : obj_idxs)
+            model_names.push_back(object(obj_idx)->name);
+    }
+    else {
+        ModelObject* obj = object(obj_idxs.front());
+        for (int i = int(vol_idxs.size()) - 1; i >= 0; --i)
+            if (obj->get_mesh_errors_count(vol_idxs[i]) == 0)
+                vol_idxs.erase(vol_idxs.begin() + i);
+        for (int vol_idx : vol_idxs)
+            model_names.push_back(obj->volumes[vol_idx]->name);
+    }
+
+    auto plater = wxGetApp().plater();
+
+    auto fix_and_update_progress = [this, plater, model_names](const int obj_idx, const int vol_idx,
+                                          int model_idx,
+                                          wxProgressDialog& progress_dlg,
+                                          std::vector<std::string>& succes_models,
+                                          std::vector<std::pair<std::string, std::string>>& failed_models)
+    {
+        const std::string& model_name = model_names[model_idx];
+        wxString msg = _L("Repairing model");
+        if (model_names.size() == 1)
+            msg += ": " + from_u8(model_name) + "\n";
+        else {
+            msg += ":\n";
+            for (size_t i = 0; i < model_names.size(); ++i)
+                msg += (i == model_idx ? " > " : "   ") + from_u8(model_names[i]) + "\n";
+            msg += "\n";
+        }
+
+        plater->clear_before_change_mesh(obj_idx);
+        std::string res;
+        if (!fix_model_by_win10_sdk_gui(*(object(obj_idx)), vol_idx, progress_dlg, msg, res))
+            return false;
+        wxGetApp().plater()->changed_mesh(obj_idx);
+
+        plater->changed_mesh(obj_idx);
+
+        if (res.empty())
+            succes_models.push_back(model_name);
+        else
+            failed_models.push_back({ model_name, res });
+
+        update_item_error_icon(obj_idx, vol_idx);
+        update_info_items(obj_idx);
+
+        return true;
+    };
+
+    Plater::TakeSnapshot snapshot(plater, _L("Fix through NetFabb"));
+
+    // Open a progress dialog.
+    wxProgressDialog progress_dlg(_L("Fixing through NetFabb"), "", 100,
+                                    nullptr, // ! parent of the wxProgressDialog should be nullptr to avoid flickering during the model fixing
+                                    wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
+    int model_idx{ 0 };
+    if (vol_idxs.empty()) {
+        int vol_idx{ -1 };
+        for (int obj_idx : obj_idxs) {
+            if (object(obj_idx)->get_mesh_errors_count(vol_idx) == 0)
+                continue;
+            if (!fix_and_update_progress(obj_idx, vol_idx, model_idx, progress_dlg, succes_models, failed_models))
+                break;
+            model_idx++;
+        }
+    }
+    else {
+        int obj_idx{ obj_idxs.front() };
+        for (int vol_idx : vol_idxs) {
+            if (!fix_and_update_progress(obj_idx, vol_idx, model_idx, progress_dlg, succes_models, failed_models))
+                break;
+            model_idx++;
+        }
+    }
+    // Close the progress dialog
+    progress_dlg.Update(100, "");
+
+    // Show info message
+    wxString msg;
+    wxString bullet_suf = "\n   - ";
+    if (!succes_models.empty()) {
+        msg = _L_PLURAL("Folowing model is repaired successfully", "Folowing models are repaired successfully", succes_models.size()) + ":";
+        for (auto& model : succes_models)
+            msg += bullet_suf + from_u8(model);
+        msg += "\n\n";
+    }
+    if (!failed_models.empty()) {
+        msg += _L_PLURAL("Folowing model repair failed", "Folowing models repair failed", failed_models.size()) + ":\n";
+        for (auto& model : failed_models)
+            msg += bullet_suf + from_u8(model.first) + ": " + _(model.second);
+    }
+    if (msg.IsEmpty())
+        msg = _L("Repairing was canceled");
+    // !!! Use wxMessageDialog instead of MessageDialog here
+    // It will not be "dark moded" but the Application will not lose a focus after model repairing
+    wxMessageDialog(nullptr, msg, _L("Model Repair by the Netfabb service"), wxICON_INFORMATION | wxOK).ShowModal();
 }
 
 void ObjectList::simplify()
diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp
index 65bcf212d..13eebd695 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -654,7 +654,6 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
     update_layers_slider_mode();
 
     Plater* plater = wxGetApp().plater();
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     CustomGCode::Info ticks_info_from_model;
     if (wxGetApp().is_editor())
         ticks_info_from_model = plater->model().custom_gcode_per_print_z;
@@ -662,18 +661,10 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
         ticks_info_from_model.mode = CustomGCode::Mode::SingleExtruder;
         ticks_info_from_model.gcodes = m_canvas->get_custom_gcode_per_print_z();
     }
-#else
-    CustomGCode::Info& ticks_info_from_model = plater->model().custom_gcode_per_print_z;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     check_layers_slider_values(ticks_info_from_model.gcodes, layers_z);
 
     //first of all update extruder colors to avoid crash, when we are switching printer preset from MM to SM
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     m_layers_slider->SetExtruderColors(plater->get_extruder_colors_from_plater_config(wxGetApp().is_editor() ? nullptr : m_gcode_result));
-#else
-    m_layers_slider->SetExtruderColors(plater->get_extruder_colors_from_plater_config());
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
-
     m_layers_slider->SetSliderValues(layers_z);
     assert(m_layers_slider->GetMinValue() == 0);
     m_layers_slider->SetMaxValue(layers_z.empty() ? 0 : layers_z.size() - 1);
@@ -943,14 +934,10 @@ void Preview::load_print_as_fff(bool keep_z_range)
         colors = wxGetApp().plater()->get_colors_for_color_print(m_gcode_result);
 
         if (!gcode_preview_data_valid) {
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
             if (wxGetApp().is_editor())
                 color_print_values = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
             else
                 color_print_values = m_canvas->get_custom_gcode_per_print_z();
-#else
-            color_print_values = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
             colors.push_back("#808080"); // gray color for pause print or custom G-code 
         }
     }
@@ -972,11 +959,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
             zs = m_canvas->get_gcode_layers_zs();
             m_loaded = true;
         }
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
         else if (wxGetApp().is_editor()) {
-#else
-        else {
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
             // Load the initial preview based on slices, not the final G-code.
             m_canvas->load_preview(colors, color_print_values);
             m_left_sizer->Hide(m_bottom_toolbar_panel);
@@ -985,7 +968,6 @@ void Preview::load_print_as_fff(bool keep_z_range)
             zs = m_canvas->get_volumes_print_zs(true);
         }
 
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
         if (!zs.empty() && !m_keep_current_preview_type) {
             unsigned int number_extruders = wxGetApp().is_editor() ?
                 (unsigned int)print->extruders().size() :
@@ -1021,7 +1003,6 @@ void Preview::load_print_as_fff(bool keep_z_range)
             }
 #endif // ENABLE_PREVIEW_LAYOUT
         }
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
 
         if (zs.empty()) {
             // all layers filtered out
@@ -1030,23 +1011,6 @@ void Preview::load_print_as_fff(bool keep_z_range)
         } else
             update_layers_slider(zs, keep_z_range);
     }
-
-#if !ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
-    if (!m_keep_current_preview_type) {
-        unsigned int number_extruders = (unsigned int)print->extruders().size();
-        const wxString choice = !wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes.empty() ?
-            _L("Color Print") :
-            (number_extruders > 1) ? _L("Tool") : _L("Feature type");
-
-        int type = m_choice_view_type->FindString(choice);
-        if (m_choice_view_type->GetSelection() != type) {
-            if (0 <= type && type < static_cast<int>(GCodeViewer::EViewType::Count)) {
-                m_choice_view_type->SetSelection(type);
-                m_canvas->set_gcode_view_preview_type(static_cast<GCodeViewer::EViewType>(type));
-            }
-        }
-    }
-#endif // !ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
 }
 
 void Preview::load_print_as_sla()
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
index 64479a39e..735328881 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
@@ -53,9 +53,9 @@ void GLGizmoBase::Grabber::render(float size, const std::array<float, 4>& render
     if (! cube_initialized) {
         // This cannot be done in constructor, OpenGL is not yet
         // initialized at that point (on Linux at least).
-        TriangleMesh mesh = make_cube(1., 1., 1.);
-        mesh.translate(Vec3f(-0.5, -0.5, -0.5));
-        const_cast<GLModel&>(cube).init_from(mesh);
+        indexed_triangle_set mesh = its_make_cube(1., 1., 1.);
+        its_translate(mesh, Vec3f(-0.5, -0.5, -0.5));
+        const_cast<GLModel&>(cube).init_from(mesh, BoundingBoxf3{ { -0.5, -0.5, -0.5 }, { 0.5, 0.5, 0.5 } });
         const_cast<bool&>(cube_initialized) = true;
     }
 
@@ -90,9 +90,9 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, u
     m_base_color = DEFAULT_BASE_COLOR;
     m_drag_color = DEFAULT_DRAG_COLOR;
     m_highlight_color = DEFAULT_HIGHLIGHT_COLOR;
-    m_cone.init_from(make_cone(1., 1., 2 * PI / 24));
-    m_sphere.init_from(make_sphere(1., (2 * M_PI) / 24.));
-    m_cylinder.init_from(make_cylinder(1., 1., 2 * PI / 24.));
+    m_cone.init_from(its_make_cone(1., 1., 2 * PI / 24));
+    m_sphere.init_from(its_make_sphere(1., (2 * M_PI) / 24.));
+    m_cylinder.init_from(its_make_cylinder(1., 1., 2 * PI / 24.));
 }
 
 void GLGizmoBase::set_hover_id(int id)
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
index 3dcb9e2b1..e4cbd77d4 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
@@ -16,9 +16,7 @@
 #include "slic3r/GUI/GUI_ObjectManipulation.hpp"
 #include "libslic3r/AppConfig.hpp"
 #include "libslic3r/Model.hpp"
-#if ENABLE_SINKING_CONTOURS
 #include "libslic3r/TriangleMeshSlicer.hpp"
-#endif // ENABLE_SINKING_CONTOURS
 
 namespace Slic3r {
 namespace GUI {
@@ -92,9 +90,7 @@ void GLGizmoCut::on_render()
     m_max_z = box.max.z();
     set_cut_z(m_cut_z);
 
-#if ENABLE_SINKING_CONTOURS
     update_contours();
-#endif // ENABLE_SINKING_CONTOURS
 
     const float min_x = box.min.x() - Margin;
     const float max_x = box.max.x() + Margin;
@@ -143,13 +139,11 @@ void GLGizmoCut::on_render()
 
     shader->stop_using();
 
-#if ENABLE_SINKING_CONTOURS
     glsafe(::glPushMatrix());
     glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z()));
     glsafe(::glLineWidth(2.0f));
     m_cut_contours.contours.render();
     glsafe(::glPopMatrix());
-#endif // ENABLE_SINKING_CONTOURS
 }
 
 void GLGizmoCut::on_render_for_picking()
@@ -275,7 +269,6 @@ BoundingBoxf3 GLGizmoCut::bounding_box() const
     return ret;
 }
 
-#if ENABLE_SINKING_CONTOURS
 void GLGizmoCut::update_contours()
 {
     const Selection& selection = m_parent.get_selection();
@@ -315,7 +308,6 @@ void GLGizmoCut::update_contours()
     else
         m_cut_contours.contours.reset();
 }
-#endif // ENABLE_SINKING_CONTOURS
 
 } // namespace GUI
 } // namespace Slic3r
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp
index b691357af..4007f89d4 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp
@@ -2,11 +2,9 @@
 #define slic3r_GLGizmoCut_hpp_
 
 #include "GLGizmoBase.hpp"
-#if ENABLE_SINKING_CONTOURS
 #include "slic3r/GUI/GLModel.hpp"
 #include "libslic3r/TriangleMesh.hpp"
 #include "libslic3r/ObjectID.hpp"
-#endif // ENABLE_SINKING_CONTOURS
 
 namespace Slic3r {
 namespace GUI {
@@ -26,7 +24,6 @@ class GLGizmoCut : public GLGizmoBase
     bool m_keep_lower{ true };
     bool m_rotate_lower{ false };
 
-#if ENABLE_SINKING_CONTOURS
     struct CutContours
     {
         TriangleMesh mesh;
@@ -39,7 +36,6 @@ class GLGizmoCut : public GLGizmoBase
     };
 
     CutContours m_cut_contours;
-#endif // ENABLE_SINKING_CONTOURS
 
 public:
     GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
@@ -66,9 +62,7 @@ private:
     void perform_cut(const Selection& selection);
     double calc_projection(const Linef3& mouse_ray) const;
     BoundingBoxf3 bounding_box() const;
-#if ENABLE_SINKING_CONTOURS
     void update_contours();
-#endif // ENABLE_SINKING_CONTOURS
 };
 
 } // namespace GUI
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
index 1ebba4d11..01eeebe10 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
@@ -287,13 +287,14 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block)
         float dot_limit = limit.dot(down);
 
         // Now calculate dot product of vert_direction and facets' normals.
-        int idx = -1;
-        for (const stl_facet &facet : mv->mesh().stl.facet_start) {
-            ++idx;
-            if (facet.normal.dot(down) > dot_limit) {
+        int idx = 0;
+        const indexed_triangle_set &its = mv->mesh().its;
+        for (stl_triangle_vertex_indices face : its.indices) {
+            if (its_face_normal(its, face).dot(down) > dot_limit) {
                 m_triangle_selectors[mesh_id]->set_facet(idx, block ? EnforcerBlockerType::BLOCKER : EnforcerBlockerType::ENFORCER);
                 m_triangle_selectors.back()->request_update_render_data();
             }
+            ++ idx;
         }
     }
 
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
index b8c7d9f92..9e30202bd 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
@@ -142,11 +142,13 @@ void GLGizmoFlatten::update_planes()
 
     // Now we'll go through all the facets and append Points of facets sharing the same normal.
     // This part is still performed in mesh coordinate system.
-    const int num_of_facets = ch.stl.stats.number_of_facets;
-    std::vector<int>  facet_queue(num_of_facets, 0);
-    std::vector<bool> facet_visited(num_of_facets, false);
-    int               facet_queue_cnt = 0;
-    const stl_normal* normal_ptr = nullptr;
+    const int                num_of_facets  = ch.facets_count();
+    const std::vector<Vec3f> face_normals   = its_face_normals(ch.its);
+    const std::vector<Vec3i> face_neighbors = its_face_neighbors(ch.its);
+    std::vector<int>         facet_queue(num_of_facets, 0);
+    std::vector<bool>        facet_visited(num_of_facets, false);
+    int                      facet_queue_cnt = 0;
+    const stl_normal*        normal_ptr      = nullptr;
     while (1) {
         // Find next unvisited triangle:
         int facet_idx = 0;
@@ -154,7 +156,7 @@ void GLGizmoFlatten::update_planes()
             if (!facet_visited[facet_idx]) {
                 facet_queue[facet_queue_cnt ++] = facet_idx;
                 facet_visited[facet_idx] = true;
-                normal_ptr = &ch.stl.facet_start[facet_idx].normal;
+                normal_ptr = &face_normals[facet_idx];
                 m_planes.emplace_back();
                 break;
             }
@@ -163,18 +165,16 @@ void GLGizmoFlatten::update_planes()
 
         while (facet_queue_cnt > 0) {
             int facet_idx = facet_queue[-- facet_queue_cnt];
-            const stl_normal& this_normal = ch.stl.facet_start[facet_idx].normal;
+            const stl_normal& this_normal = face_normals[facet_idx];
             if (std::abs(this_normal(0) - (*normal_ptr)(0)) < 0.001 && std::abs(this_normal(1) - (*normal_ptr)(1)) < 0.001 && std::abs(this_normal(2) - (*normal_ptr)(2)) < 0.001) {
-                stl_vertex* first_vertex = ch.stl.facet_start[facet_idx].vertex;
+                const Vec3i face = ch.its.indices[facet_idx];
                 for (int j=0; j<3; ++j)
-                    m_planes.back().vertices.emplace_back(first_vertex[j].cast<double>());
+                    m_planes.back().vertices.emplace_back(ch.its.vertices[face[j]].cast<double>());
 
                 facet_visited[facet_idx] = true;
-                for (int j = 0; j < 3; ++ j) {
-                    int neighbor_idx = ch.stl.neighbors_start[facet_idx].neighbor[j];
-                    if (! facet_visited[neighbor_idx])
+                for (int j = 0; j < 3; ++ j)
+                    if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && ! facet_visited[neighbor_idx])
                         facet_queue[facet_queue_cnt ++] = neighbor_idx;
-                }
             }
         }
         m_planes.back().normal = normal_ptr->cast<double>();
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
index f405f457d..17630e5c6 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
@@ -20,7 +20,7 @@ namespace GUI {
 GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
     : GLGizmoBase(parent, icon_filename, sprite_id)
 {
-    m_vbo_cylinder.init_from(make_cylinder(1., 1.));
+    m_vbo_cylinder.init_from(its_make_cylinder(1., 1.));
 }
 
 
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
index 4bd98f8c8..30f7ff7cf 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
@@ -291,6 +291,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
     m_imgui->text("");
     ImGui::Separator();
 
+    ImGui::AlignTextToFramePadding();
     m_imgui->text(m_desc.at("first_color"));
     ImGui::SameLine(combo_label_width);
     ImGui::PushItemWidth(window_width - combo_label_width - color_button_width);
@@ -302,6 +303,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
     if(ImGui::ColorEdit4("First color##color_picker", (float*)&first_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel))
         m_modified_extruders_colors[m_first_selected_extruder_idx] = {first_color.x, first_color.y, first_color.z, first_color.w};
 
+    ImGui::AlignTextToFramePadding();
     m_imgui->text(m_desc.at("second_color"));
     ImGui::SameLine(combo_label_width);
     ImGui::PushItemWidth(window_width - combo_label_width - color_button_width);
@@ -317,14 +319,12 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
 
     ImGui::Separator();
 
-    ImGui::AlignTextToFramePadding();
     m_imgui->text(m_desc.at("tool_type"));
 
     float tool_type_offset = (window_width - tool_type_radio_brush - tool_type_radio_bucket_fill - tool_type_radio_smart_fill + m_imgui->scaled(2.f)) / 2.f;
 
     ImGui::NewLine();
 
-    ImGui::AlignTextToFramePadding();
     ImGui::SameLine(tool_type_offset + m_imgui->scaled(0.f));
     ImGui::PushItemWidth(tool_type_radio_brush);
     if (m_imgui->radio_button(m_desc["tool_brush"], m_tool_type == GLGizmoMmuSegmentation::ToolType::BRUSH)) {
@@ -382,12 +382,10 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
     ImGui::Separator();
 
     if(m_tool_type == ToolType::BRUSH) {
-        ImGui::AlignTextToFramePadding();
         m_imgui->text(m_desc.at("cursor_type"));
         ImGui::NewLine();
 
         float cursor_type_offset = (window_width - cursor_type_radio_sphere - cursor_type_radio_circle - cursor_type_radio_pointer + m_imgui->scaled(2.f)) / 2.f;
-        ImGui::AlignTextToFramePadding();
         ImGui::SameLine(cursor_type_offset + m_imgui->scaled(0.f));
         ImGui::PushItemWidth(cursor_type_radio_sphere);
         if (m_imgui->radio_button(m_desc["sphere"], m_cursor_type == TriangleSelector::CursorType::SPHERE))
@@ -458,8 +456,8 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
 
         ImGui::Separator();
     } else if(m_tool_type == ToolType::SMART_FILL) {
-        m_imgui->text(m_desc["smart_fill_angle"] + ":");
         ImGui::AlignTextToFramePadding();
+        m_imgui->text(m_desc["smart_fill_angle"] + ":");
         std::string format_str = std::string("%.f") + I18N::translate_utf8("°", "Degree sign to use in the respective slider in MMU gizmo,"
                                                                                 "placed after the number with no whitespace in between.");
         ImGui::SameLine(sliders_width);
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
index e7b6a0ad6..e73a85647 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
@@ -20,7 +20,7 @@ GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filenam
     , m_starting_box_center(Vec3d::Zero())
     , m_starting_box_bottom_center(Vec3d::Zero())
 {
-    m_vbo_cone.init_from(make_cone(1., 1., 2*PI/36));
+    m_vbo_cone.init_from(its_make_cone(1., 1., 2*PI/36));
 }
 
 std::string GLGizmoMove3D::get_tooltip() const
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
index 155738b32..aac4d6ff7 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
@@ -8,8 +8,9 @@
 #include "slic3r/GUI/GUI_App.hpp"
 #include "slic3r/GUI/Camera.hpp"
 #include "slic3r/GUI/Plater.hpp"
-#include "libslic3r/PresetBundle.hpp"
 #include "libslic3r/Model.hpp"
+#include "libslic3r/PresetBundle.hpp"
+#include "libslic3r/TriangleMesh.hpp"
 
 
 
@@ -20,17 +21,10 @@ GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& ic
     : GLGizmoBase(parent, icon_filename, sprite_id)
 {
     // Make sphere and save it into a vertex buffer.
-    const TriangleMesh sphere_mesh = make_sphere(1., (2*M_PI)/24.);
-    for (size_t i=0; i<sphere_mesh.its.vertices.size(); ++i)
-        m_vbo_sphere.push_geometry(sphere_mesh.its.vertices[i].cast<double>(),
-                                    sphere_mesh.stl.facet_start[i].normal.cast<double>());
-    for (const stl_triangle_vertex_indices& indices : sphere_mesh.its.indices)
-        m_vbo_sphere.push_triangle(indices(0), indices(1), indices(2));
+    m_vbo_sphere.load_its_flat_shading(its_make_sphere(1., (2*M_PI)/24.));
     m_vbo_sphere.finalize_geometry(true);
 }
 
-
-
 // port of 948bc382655993721d93d3b9fce9b0186fcfb211
 void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate)
 {
@@ -632,9 +626,15 @@ void TriangleSelectorGUI::update_render_data()
 
         GLIndexedVertexArray &iva = tr.get_state() == EnforcerBlockerType::ENFORCER ? m_iva_enforcers : m_iva_blockers;
         int &                 cnt = tr.get_state() == EnforcerBlockerType::ENFORCER ? enf_cnt : blc_cnt;
-
-        for (int i = 0; i < 3; ++i)
-            iva.push_geometry(m_vertices[tr.verts_idxs[i]].v, m_mesh->stl.facet_start[tr.source_triangle].normal);
+        const Vec3f          &v0  = m_vertices[tr.verts_idxs[0]].v;
+        const Vec3f          &v1  = m_vertices[tr.verts_idxs[1]].v;
+        const Vec3f          &v2  = m_vertices[tr.verts_idxs[2]].v;
+        //FIXME the normal may likely be pulled from m_triangle_selectors, but it may not be worth the effort 
+        // or the current implementation may be more cache friendly.
+        const Vec3f           n   = (v1 - v0).cross(v2 - v1).normalized();
+        iva.push_geometry(v0, n);
+        iva.push_geometry(v1, n);
+        iva.push_geometry(v2, n);
         iva.push_triangle(cnt, cnt + 1, cnt + 2);
         cnt += 3;
     }
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index 11446d6d7..a50c503b9 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -881,7 +881,7 @@ CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const
 
 void GLGizmoSlaSupports::ask_about_changes_call_after(std::function<void()> on_yes, std::function<void()> on_no)
 {
-    wxGetApp().CallAfter([this, on_yes, on_no]() {
+    wxGetApp().CallAfter([on_yes, on_no]() {
         // Following is called through CallAfter, because otherwise there was a problem
         // on OSX with the wxMessageDialog being shown several times when clicked into.
         MessageDialog dlg(GUI::wxGetApp().mainframe, _L("Do you want to save your manually "
diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp
index 65c326116..767abd7fd 100644
--- a/src/slic3r/GUI/MeshUtils.hpp
+++ b/src/slic3r/GUI/MeshUtils.hpp
@@ -30,11 +30,8 @@ public:
 
     ClippingPlane(const Vec3d& direction, double offset)
     {
-        Vec3d norm_dir = direction.normalized();
-        m_data[0] = norm_dir(0);
-        m_data[1] = norm_dir(1);
-        m_data[2] = norm_dir(2);
-        m_data[3] = offset;
+        set_normal(direction);
+        set_offset(offset);
     }
 
     bool operator==(const ClippingPlane& cp) const {
@@ -48,7 +45,13 @@ public:
     }
 
     bool is_point_clipped(const Vec3d& point) const { return distance(point) < 0.; }
-    void set_normal(const Vec3d& normal) { for (size_t i=0; i<3; ++i) m_data[i] = normal(i); }
+    void set_normal(const Vec3d& normal)
+    {
+        const Vec3d norm_dir = normal.normalized();
+        m_data[0] = norm_dir.x();
+        m_data[1] = norm_dir.y();
+        m_data[2] = norm_dir.z();
+    }
     void set_offset(double offset) { m_data[3] = offset; }
     Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); }
     bool is_active() const { return m_data[3] != DBL_MAX; }
@@ -113,10 +116,8 @@ public:
     // during MeshRaycaster existence.
     MeshRaycaster(const TriangleMesh& mesh)
         : m_emesh(mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length
+        , m_normals(its_face_normals(mesh.its))
     {
-        m_normals.reserve(mesh.stl.facet_start.size());
-        for (const stl_facet& facet : mesh.stl.facet_start)
-            m_normals.push_back(facet.normal);
     }
 
     void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp
index 0841efcb0..bf43b7645 100644
--- a/src/slic3r/GUI/NotificationManager.cpp
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -1176,7 +1176,9 @@ void NotificationManager::SlicingProgressNotification::set_sidebar_collapsed(boo
 void NotificationManager::SlicingProgressNotification::on_cancel_button()
 {
 	if (m_cancel_callback){
-		m_cancel_callback();
+		if (!m_cancel_callback()) {
+			set_progress_state(SlicingProgressState::SP_NO_SLICING);
+		}
 	}
 }
 int NotificationManager::SlicingProgressNotification::get_duration()
@@ -1681,7 +1683,7 @@ void NotificationManager::upload_job_notification_show_error(int id, const std::
 	}
 }
 
-void NotificationManager::init_slicing_progress_notification(std::function<void()> cancel_callback)
+void NotificationManager::init_slicing_progress_notification(std::function<bool()> cancel_callback)
 {
 	for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
 		if (notification->get_type() == NotificationType::SlicingProgress) {
diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp
index 76dc9a688..ee510b112 100644
--- a/src/slic3r/GUI/NotificationManager.hpp
+++ b/src/slic3r/GUI/NotificationManager.hpp
@@ -188,7 +188,7 @@ public:
 	void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host);
 	void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host);
 	// slicing progress
-	void init_slicing_progress_notification(std::function<void()> cancel_callback);
+	void init_slicing_progress_notification(std::function<bool()> cancel_callback);
 	// percentage negative = canceled, <0-1) = progress, 1 = completed 
 	void set_slicing_progress_percentage(const std::string& text, float percentage);
 	// hides slicing progress notification imidietly
@@ -496,7 +496,7 @@ private:
 			SP_CANCELLED, // fades after 10 seconds, simple message
 			SP_COMPLETED // Has export hyperlink and print info, fades after 20 sec if sidebar is shown, otherwise no fade out
 		};
-		SlicingProgressNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, std::function<void()> callback)
+		SlicingProgressNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, std::function<bool()> callback)
 		: ProgressBarNotification(n, id_provider, evt_handler)
 		, m_cancel_callback(callback)
 		{
@@ -507,7 +507,7 @@ private:
 		// sets text of notification - call after setting progress state
 		void				set_status_text(const std::string& text);
 		// sets cancel button callback
-		void			    set_cancel_callback(std::function<void()> callback) { m_cancel_callback = callback; }
+		void			    set_cancel_callback(std::function<bool()> callback) { m_cancel_callback = callback; }
 		bool                has_cancel_callback() const { return m_cancel_callback != nullptr; }
 		// sets SlicingProgressState, negative percent means canceled
 		void				set_progress_state(float percent);
@@ -545,7 +545,8 @@ private:
 									const float win_pos_x, const float win_pos_y) override;
 		void       on_cancel_button();
 		int		   get_duration() override;
-		std::function<void()>	m_cancel_callback;
+		// if returns false, process was already canceled
+		std::function<bool()>	m_cancel_callback;
 		SlicingProgressState	m_sp_state { SlicingProgressState::SP_PROGRESS };
 		bool				    m_has_print_info { false };
 		std::string             m_print_info;
diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp
index 928249e5b..8ceb3cc23 100644
--- a/src/slic3r/GUI/ObjectDataViewModel.cpp
+++ b/src/slic3r/GUI/ObjectDataViewModel.cpp
@@ -37,7 +37,6 @@ void ObjectDataViewModelNode::init_container()
 static constexpr char LayerRootIcon[]   = "edit_layers_all";
 static constexpr char LayerIcon[]       = "edit_layers_some";
 static constexpr char WarningIcon[]     = "exclamation";
-static constexpr char InfoIcon[]        = "objlist_info";
 
 struct InfoItemAtributes {
     std::string name;
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 76e2e07f4..704fe94a1 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -1722,7 +1722,6 @@ struct Plater::priv
 #endif // ENABLE_RELOAD_FROM_DISK_REPLACE_FILE
     void replace_with_stl();
     void reload_all_from_disk();
-    void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
     void create_simplify_notification(const std::vector<size_t>& obj_ids);
     void set_current_panel(wxPanel* panel);
 
@@ -3664,27 +3663,6 @@ void Plater::priv::reload_all_from_disk()
     }
 }
 
-void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/)
-{
-    if (obj_idx < 0)
-        return;
-
-    // Do not fix anything when a gizmo is open. There might be issues with updates
-    // and what is worse, the snapshot time would refer to the internal stack.
-    if (! q->canvas3D()->get_gizmos_manager().check_gizmos_closed_except(GLGizmosManager::Undefined))
-        return;
-
-    // size_t snapshot_time = undo_redo_stack().active_snapshot_time();
-    Plater::TakeSnapshot snapshot(q, _L("Fix through NetFabb"));
-
-    q->clear_before_change_mesh(obj_idx);
-    ModelObject* mo = model.objects[obj_idx];
-    fix_model_by_win10_sdk_gui(*mo, vol_idx);
-    q->changed_mesh(obj_idx);
-    // workaround to fix the issue, when PrusaSlicer lose a focus after model fixing
-    q->SetFocus();
-}
-
 void Plater::priv::create_simplify_notification(const std::vector<size_t>& obj_ids) {
     const uint32_t triangles_to_suggest_simplify = 1000000;
 
@@ -4041,12 +4019,14 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
     if (evt.error()) {
         std::pair<std::string, bool> message = evt.format_error_message();
         if (evt.critical_error()) {
-            if (q->m_tracking_popup_menu)
+            if (q->m_tracking_popup_menu) {
                 // We don't want to pop-up a message box when tracking a pop-up menu.
                 // We postpone the error message instead.
                 q->m_tracking_popup_menu_error_message = message.first;
-            else
+            } else {
                 show_error(q, message.first, message.second);
+                notification_manager->set_slicing_progress_hidden();
+            }
         } else
             notification_manager->push_slicing_error_notification(message.first);
 //        this->statusbar()->set_status_text(from_u8(message.first));
@@ -4168,7 +4148,8 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
             const bool is_some_full_instances = get_selection().is_single_full_instance() || 
                                                 get_selection().is_single_full_object() || 
                                                 get_selection().is_multiple_full_instance();
-            menu = is_some_full_instances ? menus.object_menu() : menus.part_menu();
+            menu = is_some_full_instances               ? menus.object_menu() : 
+                   get_selection().is_single_volume()   ? menus.part_menu()   : menus.multi_selection_menu();
         }
     }
 
@@ -4273,7 +4254,10 @@ void Plater::priv::init_notification_manager()
     notification_manager->init();
 
     auto cancel_callback = [this]() {
+        if (this->background_process.idle())
+            return false;
         this->background_process.stop();
+        return true;
     };
     notification_manager->init_slicing_progress_notification(cancel_callback);
     notification_manager->set_fff(printer_technology == ptFFF);
@@ -4521,11 +4505,22 @@ bool Plater::priv::can_delete_all() const
 
 bool Plater::priv::can_fix_through_netfabb() const
 {
-    int obj_idx = get_selected_object_idx();
-    if (obj_idx < 0)
-        return false;
+    std::vector<int> obj_idxs, vol_idxs;
+    sidebar->obj_list()->get_selection_indexes(obj_idxs, vol_idxs);
 
-    return model.objects[obj_idx]->get_mesh_errors_count() > 0;
+    if (vol_idxs.empty()) {
+        for (auto obj_idx : obj_idxs)
+            if (model.objects[obj_idx]->get_mesh_errors_count() > 0)
+                return true;
+        return false;
+    }
+
+    int obj_idx = obj_idxs.front();
+    for (auto vol_idx : vol_idxs)
+        if (model.objects[obj_idx]->get_mesh_errors_count(vol_idx) > 0)
+            return true;
+
+    return false;
 }
 
 
@@ -6233,7 +6228,6 @@ std::vector<std::string> Plater::get_colors_for_color_print(const GCodeProcessor
     std::vector<std::string> colors = get_extruder_colors_from_plater_config(result);
     colors.reserve(colors.size() + p->model.custom_gcode_per_print_z.gcodes.size());
 
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     if (wxGetApp().is_gcode_viewer() && result != nullptr) {
         for (const CustomGCode::Item& code : result->custom_gcode_per_print_z) {
             if (code.type == CustomGCode::ColorChange)
@@ -6241,14 +6235,11 @@ std::vector<std::string> Plater::get_colors_for_color_print(const GCodeProcessor
         }
     }
     else {
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
         for (const CustomGCode::Item& code : p->model.custom_gcode_per_print_z.gcodes) {
             if (code.type == CustomGCode::ColorChange)
                 colors.emplace_back(code.color);
         }
-#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
     }
-#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
 
     return colors;
 }
@@ -6472,7 +6463,6 @@ void Plater::suppress_background_process(const bool stop_background_process)
     this->p->suppressed_backround_processing_update = true;
 }
 
-void Plater::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) { p->fix_through_netfabb(obj_idx, vol_idx); }
 void Plater::mirror(Axis axis)      { p->mirror(axis); }
 void Plater::split_object()         { p->split_object(); }
 void Plater::split_volume()         { p->split_volume(); }
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index dffb2451f..bfee3e0f6 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -235,7 +235,6 @@ public:
     void schedule_background_process(bool schedule = true);
     bool is_background_process_update_scheduled() const;
     void suppress_background_process(const bool stop_background_process) ;
-    void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
     void send_gcode();
 	void eject_drive();
 
diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp
index 53c93b6b0..d8af0e887 100644
--- a/src/slic3r/GUI/Preferences.cpp
+++ b/src/slic3r/GUI/Preferences.cpp
@@ -619,8 +619,8 @@ void PreferencesDialog::create_settings_mode_widget()
             m_values["old_settings_layout_mode"] = (id == 0) ? "1" : "0";
 #ifdef _MSW_DARK_MODE
 			if (!disable_new_layout)
-            m_values["new_settings_layout_mode"] = (id == 1) ? "1" : "0";
 #endif
+            m_values["new_settings_layout_mode"] = (id == 1) ? "1" : "0";
             m_values["dlg_settings_layout_mode"] = (id == dlg_id) ? "1" : "0";
 		});
 		id++;
diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp
index 92c758d4b..4a9c7cd56 100644
--- a/src/slic3r/GUI/Selection.cpp
+++ b/src/slic3r/GUI/Selection.cpp
@@ -620,24 +620,54 @@ const GLVolume* Selection::get_volume(unsigned int volume_idx) const
 
 const BoundingBoxf3& Selection::get_bounding_box() const
 {
-    if (m_bounding_box_dirty)
-        calc_bounding_box();
-
-    return m_bounding_box;
+    if (!m_bounding_box.has_value()) {
+        std::optional<BoundingBoxf3>* bbox = const_cast<std::optional<BoundingBoxf3>*>(&m_bounding_box);
+        *bbox = BoundingBoxf3();
+        if (m_valid) {
+            for (unsigned int i : m_list) {
+                (*bbox)->merge((*m_volumes)[i]->transformed_convex_hull_bounding_box());
+            }
+        }
+    }
+    return *m_bounding_box;
 }
 
 const BoundingBoxf3& Selection::get_unscaled_instance_bounding_box() const
 {
-    if (m_unscaled_instance_bounding_box_dirty)
-        calc_unscaled_instance_bounding_box();
-    return m_unscaled_instance_bounding_box;
+    if (!m_unscaled_instance_bounding_box.has_value()) {
+        std::optional<BoundingBoxf3>* bbox = const_cast<std::optional<BoundingBoxf3>*>(&m_unscaled_instance_bounding_box);
+        *bbox = BoundingBoxf3();
+        if (m_valid) {
+            for (unsigned int i : m_list) {
+                const GLVolume& volume = *(*m_volumes)[i];
+                if (volume.is_modifier)
+                    continue;
+                Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, true, false) * volume.get_volume_transformation().get_matrix();
+                trafo.translation().z() += volume.get_sla_shift_z();
+                (*bbox)->merge(volume.transformed_convex_hull_bounding_box(trafo));
+            }
+        }
+    }
+    return *m_unscaled_instance_bounding_box;
 }
 
 const BoundingBoxf3& Selection::get_scaled_instance_bounding_box() const
 {
-    if (m_scaled_instance_bounding_box_dirty)
-        calc_scaled_instance_bounding_box();
-    return m_scaled_instance_bounding_box;
+    if (!m_scaled_instance_bounding_box.has_value()) {
+        std::optional<BoundingBoxf3>* bbox = const_cast<std::optional<BoundingBoxf3>*>(&m_scaled_instance_bounding_box);
+        *bbox = BoundingBoxf3();
+        if (m_valid) {
+            for (unsigned int i : m_list) {
+                const GLVolume& volume = *(*m_volumes)[i];
+                if (volume.is_modifier)
+                    continue;
+                Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, false, false) * volume.get_volume_transformation().get_matrix();
+                trafo.translation().z() += volume.get_sla_shift_z();
+                (*bbox)->merge(volume.transformed_convex_hull_bounding_box(trafo));
+            }
+        }
+    }
+    return *m_scaled_instance_bounding_box;
 }
 
 void Selection::start_dragging()
@@ -823,10 +853,10 @@ void Selection::flattening_rotate(const Vec3d& normal)
     }
 
 #if !DISABLE_INSTANCES_SYNCH
-    // we want to synchronize z-rotation as well, otherwise the flattening behaves funny
-    // when applied on one of several identical instances
+    // Apply the same transformation also to other instances,
+    // but respect their possibly diffrent z-rotation.
     if (m_mode == Instance)
-        synchronize_unselected_instances(SYNC_ROTATION_FULL);
+        synchronize_unselected_instances(SYNC_ROTATION_GENERAL);
 #endif // !DISABLE_INSTANCES_SYNCH
 
     this->set_bounding_boxes_dirty();
@@ -1692,52 +1722,6 @@ void Selection::do_remove_object(unsigned int object_idx)
     }
 }
 
-void Selection::calc_bounding_box() const
-{
-    BoundingBoxf3* bounding_box = const_cast<BoundingBoxf3*>(&m_bounding_box);
-    *bounding_box = BoundingBoxf3();
-    if (m_valid) {
-        for (unsigned int i : m_list) {
-            bounding_box->merge((*m_volumes)[i]->transformed_convex_hull_bounding_box());
-        }
-    }
-    *const_cast<bool*>(&m_bounding_box_dirty) = false;
-}
-
-void Selection::calc_unscaled_instance_bounding_box() const
-{
-    BoundingBoxf3* unscaled_instance_bounding_box = const_cast<BoundingBoxf3*>(&m_unscaled_instance_bounding_box);
-    *unscaled_instance_bounding_box = BoundingBoxf3();
-    if (m_valid) {
-        for (unsigned int i : m_list) {
-            const GLVolume& volume = *(*m_volumes)[i];
-            if (volume.is_modifier)
-                continue;
-            Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, true, false) * volume.get_volume_transformation().get_matrix();
-            trafo.translation().z() += volume.get_sla_shift_z();
-            unscaled_instance_bounding_box->merge(volume.transformed_convex_hull_bounding_box(trafo));
-        }
-    }
-    *const_cast<bool*>(&m_unscaled_instance_bounding_box_dirty) = false;
-}
-
-void Selection::calc_scaled_instance_bounding_box() const
-{
-    BoundingBoxf3* scaled_instance_bounding_box = const_cast<BoundingBoxf3*>(&m_scaled_instance_bounding_box);
-    *scaled_instance_bounding_box = BoundingBoxf3();
-    if (m_valid) {
-        for (unsigned int i : m_list) {
-            const GLVolume& volume = *(*m_volumes)[i];
-            if (volume.is_modifier)
-                continue;
-            Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, false, false) * volume.get_volume_transformation().get_matrix();
-            trafo.translation().z() += volume.get_sla_shift_z();
-            scaled_instance_bounding_box->merge(volume.transformed_convex_hull_bounding_box(trafo));
-        }
-    }
-    *const_cast<bool*>(&m_scaled_instance_bounding_box_dirty) = false;
-}
-
 void Selection::render_selected_volumes() const
 {
     float color[3] = { 1.0f, 1.0f, 1.0f };
@@ -2055,10 +2039,6 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_
                     v->set_instance_offset(Z, volume->get_instance_offset().z());
                 break;
             }
-            case SYNC_ROTATION_FULL:
-                // rotation comes from place on face -> force given z
-                v->set_instance_rotation({ rotation.x(), rotation.y(), rotation.z() });
-                break;
             case SYNC_ROTATION_GENERAL:
                 // generic rotation -> update instance z with the delta of the rotation.
                 const double z_diff = Geometry::rotation_diff_z(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation());
diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp
index b3304571f..dea507511 100644
--- a/src/slic3r/GUI/Selection.hpp
+++ b/src/slic3r/GUI/Selection.hpp
@@ -1,10 +1,12 @@
 #ifndef slic3r_GUI_Selection_hpp_
 #define slic3r_GUI_Selection_hpp_
 
-#include <set>
 #include "libslic3r/Geometry.hpp"
 #include "GLModel.hpp"
 
+#include <set>
+#include <optional>
+
 namespace Slic3r {
 
 class Shader;
@@ -203,14 +205,11 @@ private:
     IndicesList m_list;
     Cache m_cache;
     Clipboard m_clipboard;
-    BoundingBoxf3 m_bounding_box;
-    bool m_bounding_box_dirty;
+    std::optional<BoundingBoxf3> m_bounding_box;
     // Bounding box of a selection, with no instance scaling applied. This bounding box
     // is useful for absolute scaling of tilted objects in world coordinate space.
-    BoundingBoxf3 m_unscaled_instance_bounding_box;
-    bool m_unscaled_instance_bounding_box_dirty;
-    BoundingBoxf3 m_scaled_instance_bounding_box;
-    bool m_scaled_instance_bounding_box_dirty;
+    std::optional<BoundingBoxf3> m_unscaled_instance_bounding_box;
+    std::optional<BoundingBoxf3> m_scaled_instance_bounding_box;
 
 #if ENABLE_RENDER_SELECTION_CENTER
     GLModel m_vbo_sphere;
@@ -359,10 +358,7 @@ private:
     void do_remove_volume(unsigned int volume_idx);
     void do_remove_instance(unsigned int object_idx, unsigned int instance_idx);
     void do_remove_object(unsigned int object_idx);
-    void calc_bounding_box() const;
-    void calc_unscaled_instance_bounding_box() const;
-    void calc_scaled_instance_bounding_box() const;
-    void set_bounding_boxes_dirty() { m_bounding_box_dirty = true; m_unscaled_instance_bounding_box_dirty = true; m_scaled_instance_bounding_box_dirty = true; }
+    void set_bounding_boxes_dirty() { m_bounding_box.reset(); m_unscaled_instance_bounding_box.reset(); m_scaled_instance_bounding_box.reset(); }
     void render_selected_volumes() const;
     void render_synchronized_volumes() const;
     void render_bounding_box(const BoundingBoxf3& box, float* color) const;
@@ -375,10 +371,8 @@ public:
     enum SyncRotationType {
         // Do not synchronize rotation. Either not rotating at all, or rotating by world Z axis.
         SYNC_ROTATION_NONE = 0,
-        // Synchronize fully. Used from "place on bed" feature.
-        SYNC_ROTATION_FULL = 1,
         // Synchronize after rotation by an axis not parallel with Z.
-        SYNC_ROTATION_GENERAL = 2,
+        SYNC_ROTATION_GENERAL = 1,
     };
     void synchronize_unselected_instances(SyncRotationType sync_rotation_type);
     void synchronize_unselected_volumes();
diff --git a/src/slic3r/Utils/FixModelByWin10.cpp b/src/slic3r/Utils/FixModelByWin10.cpp
index 2fbd0d937..931d2f449 100644
--- a/src/slic3r/Utils/FixModelByWin10.cpp
+++ b/src/slic3r/Utils/FixModelByWin10.cpp
@@ -318,7 +318,10 @@ public:
    const char* what() const throw() { return "Model repair has been canceled"; }
 };
 
-void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
+// returt FALSE, if fixing was canceled
+// fix_result is empty, if fixing finished successfully
+// fix_result containes a message if fixing failed 
+bool fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx, wxProgressDialog& progress_dialog, const wxString& msg_header, std::string& fix_result)
 {
 	std::mutex 						mutex;
 	std::condition_variable			condition;
@@ -337,11 +340,6 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
 	else
 		volumes.emplace_back(model_object.volumes[volume_idx]);
 
-	// Open a progress dialog.
-	wxProgressDialog progress_dialog(
-		_L("Model fixing"),
-		_L("Exporting model") + "...",
-		100, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);  // ! parent of the wxProgressDialog should be nullptr to avoid flickering during the model fixing
 	// Executing the calculation in a background thread, so that the COM context could be created with its own threading model.
 	// (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context).
 	bool   success = false;
@@ -423,21 +421,23 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
 	});
     while (! finished) {
 		condition.wait_for(lock, std::chrono::milliseconds(500), [&progress]{ return progress.updated; });
-		if (! progress_dialog.Update(progress.percent, _(progress.message)))
+		// decrease progress.percent value to avoid closing of the progress dialog
+		if (!progress_dialog.Update(progress.percent-1, msg_header + _(progress.message)))
 			canceled = true;
+		else
+			progress_dialog.Fit();
 		progress.updated = false;
     }
 
 	if (canceled) {
 		// Nothing to show.
 	} else if (success) {
-		Slic3r::GUI::MessageDialog dlg(nullptr, _L("Model repaired successfully"), _L("Model Repair by the Netfabb service"), wxICON_INFORMATION | wxOK);
-		dlg.ShowModal();
+		fix_result = "";
 	} else {
-		Slic3r::GUI::MessageDialog dlg(nullptr, _L("Model repair failed:") + " \n" + _(progress.message), _L("Model Repair by the Netfabb service"), wxICON_ERROR | wxOK);
-		dlg.ShowModal();
+		fix_result = progress.message;
 	}
 	worker_thread.join();
+	return !canceled;
 }
 
 } // namespace Slic3r
diff --git a/src/slic3r/Utils/FixModelByWin10.hpp b/src/slic3r/Utils/FixModelByWin10.hpp
index 8e4766467..a1ef4511b 100644
--- a/src/slic3r/Utils/FixModelByWin10.hpp
+++ b/src/slic3r/Utils/FixModelByWin10.hpp
@@ -3,6 +3,8 @@
 
 #include <string>
 
+class wxProgressDialog;
+
 namespace Slic3r {
 
 class Model;
@@ -12,12 +14,14 @@ class Print;
 #ifdef HAS_WIN10SDK
 
 extern bool is_windows10();
-extern void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx);
+// returt false, if fixing was canceled
+extern bool fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx, wxProgressDialog& progress_dlg, const wxString& msg_header, std::string& fix_result);
 
 #else /* HAS_WIN10SDK */
 
 inline bool is_windows10() { return false; }
-inline void fix_model_by_win10_sdk_gui(ModelObject &, int) {}
+// returt false, if fixing was canceled
+inline bool fix_model_by_win10_sdk_gui(ModelObject&, int, wxProgressDialog&, const wxString&, std::string&) { return false; }
 
 #endif /* HAS_WIN10SDK */
 
diff --git a/tests/fff_print/test_trianglemesh.cpp b/tests/fff_print/test_trianglemesh.cpp
index fa6237c8b..df237db96 100644
--- a/tests/fff_print/test_trianglemesh.cpp
+++ b/tests/fff_print/test_trianglemesh.cpp
@@ -342,7 +342,7 @@ SCENARIO( "TriangleMesh: Mesh merge functions") {
             cube.merge(cube2);
             cube.repair();
             THEN( "There are twice as many facets in the merged mesh as the original.") {
-                REQUIRE(cube.stl.stats.number_of_facets == 2 * cube2.stl.stats.number_of_facets);
+                REQUIRE(cube.facets_count() == 2 * cube2.facets_count());
             }
         }
     }
diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp
index e7a171efd..35fbb48ee 100644
--- a/xs/xsp/Model.xsp
+++ b/xs/xsp/Model.xsp
@@ -10,7 +10,6 @@
 #include "libslic3r/Format/AMF.hpp"
 #include "libslic3r/Format/3mf.hpp"
 #include "libslic3r/Format/OBJ.hpp"
-#include "libslic3r/Format/PRUS.hpp"
 #include "libslic3r/Format/STL.hpp"
 #include "libslic3r/PresetBundle.hpp"
 %}