diff --git a/CMake/mitkFunctionGetLibrarySearchPaths.cmake b/CMake/mitkFunctionGetLibrarySearchPaths.cmake
index 86725d7193..21f2436c0e 100644
--- a/CMake/mitkFunctionGetLibrarySearchPaths.cmake
+++ b/CMake/mitkFunctionGetLibrarySearchPaths.cmake
@@ -1,213 +1,219 @@
 #! Helper function that gets all library search paths.
 #!
 #! Usage:
 #!
 #!   mitkFunctionGetLibrarySearchPaths(search_path intermediate_dir [DEBUG|MINSIZEREL|RELWITHDEBINFO])
 #!
 #!
 #! The function creates the variable ${search_path}. The variable intermediate_dir contains
 #! paths that should be added to the search_path but should not be checked for existance,
 #! because the are not yet created. The option DEBUG, MINSIZEREL or RELWITHDEBINFO can be used to indicate that
 #! not the paths for release configuration are requested but the debug, min size release or "release with debug info"
 #! paths.
 #!
 
 function(mitkFunctionGetLibrarySearchPaths search_path intermediate_dir)
 
   cmake_parse_arguments(PARSE_ARGV 2 GLS "RELEASE;DEBUG;MINSIZEREL;RELWITHDEBINFO" "" "")
 
   set(_dir_candidates
       "${MITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
       "${MITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY}/plugins"
       "${MITK_CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
       "${MITK_CMAKE_LIBRARY_OUTPUT_DIRECTORY}/plugins"
      )
   if(MITK_EXTERNAL_PROJECT_PREFIX)
     list(APPEND _dir_candidates
          "${MITK_EXTERNAL_PROJECT_PREFIX}/bin"
          "${MITK_EXTERNAL_PROJECT_PREFIX}/lib"
         )
   endif()
 
   # Determine the Qt5 library installation prefix
   set(_qmake_location )
   if(MITK_USE_Qt5 AND TARGET ${Qt5Core_QMAKE_EXECUTABLE})
     get_property(_qmake_location TARGET ${Qt5Core_QMAKE_EXECUTABLE}
                  PROPERTY IMPORT_LOCATION)
   endif()
   if(_qmake_location)
     if(NOT _qt_install_libs)
       if(WIN32)
         execute_process(COMMAND ${_qmake_location} -query QT_INSTALL_BINS
                         OUTPUT_VARIABLE _qt_install_libs
                         OUTPUT_STRIP_TRAILING_WHITESPACE)
       else()
         execute_process(COMMAND ${_qmake_location} -query QT_INSTALL_LIBS
                         OUTPUT_VARIABLE _qt_install_libs
                         OUTPUT_STRIP_TRAILING_WHITESPACE)
       endif()
       file(TO_CMAKE_PATH "${_qt_install_libs}" _qt_install_libs)
       set(_qt_install_libs ${_qt_install_libs} CACHE INTERNAL "Qt library installation prefix" FORCE)
     endif()
     if(_qt_install_libs)
       list(APPEND _dir_candidates ${_qt_install_libs})
     endif()
   elseif(MITK_USE_Qt5)
     message(WARNING "The qmake executable could not be found.")
   endif()
 
   get_property(_additional_paths GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS)
 
   if(TARGET OpenSSL::SSL)
     if(GLS_DEBUG)
       get_target_property(_openssl_location OpenSSL::SSL IMPORTED_LOCATION_DEBUG)
     else()
       get_target_property(_openssl_location OpenSSL::SSL IMPORTED_LOCATION_RELEASE)
     endif()
     if(_openssl_location)
       get_filename_component(_openssl_location ${_openssl_location} DIRECTORY)
       set(_openssl_location "${_openssl_location}/../../bin")
       if(EXISTS ${_openssl_location})
         get_filename_component(_openssl_location ${_openssl_location} ABSOLUTE)
         list(APPEND _dir_candidates ${_openssl_location})
       endif()
     endif()
   endif()
 
   if(MITK_USE_HDF5)
     FIND_PACKAGE(HDF5 COMPONENTS C HL NO_MODULE REQUIRED shared)
     get_target_property(_location hdf5-shared LOCATION)
     get_filename_component(_location ${_location} PATH)
     list(APPEND _additional_paths ${_location})
 
     # This is a work-around. The hdf5-config.cmake file is not robust enough
     # to be included several times via find_pakcage calls.
     set(HDF5_LIBRARIES ${HDF5_LIBRARIES} PARENT_SCOPE)
   endif()
   if(MITK_USE_Vigra)
     # we cannot use _find_package(Vigra) here because the vigra-config.cmake file
     # always includes the target-exports files without using an include guard. This
     # would lead to errors when another find_package(Vigra) call is processed. The
     # (bad) assumption here is that for the time being, only the Classification module
     # is using Vigra.
     if(UNIX)
       list(APPEND _additional_paths ${Vigra_DIR}/lib)
     else()
       list(APPEND _additional_paths ${Vigra_DIR}/bin)
     endif()
   endif()
 
   if(_additional_paths)
     list(APPEND _dir_candidates ${_additional_paths})
   endif()
 
   # The code below is sub-optimal. It makes assumptions about
   # the structure of the build directories, pointed to by
   # the *_DIR variables. Instead, we should rely on package
   # specific "LIBRARY_DIRS" variables, if they exist.
   if(WIN32)
     list(APPEND _dir_candidates "${ITK_DIR}/bin")
   endif()
 
   if(MITK_USE_MatchPoint)
     if(WIN32)
       list(APPEND _dir_candidates "${MatchPoint_DIR}/bin")
     else()
       list(APPEND _dir_candidates "${MatchPoint_DIR}/lib")
     endif()
   endif()
 
   # If OpenCV is built within the MITK superbuild set the binary directory
   # according to the lib path provided by OpenCV.
   # In the case where an external OpenCV is provided use the binary directory
   #  of this OpenCV directory
   if(MITK_USE_OpenCV)
     if(WIN32)
       if (EXISTS ${OpenCV_LIB_PATH})
         list(APPEND _dir_candidates "${OpenCV_LIB_PATH}/../bin") # OpenCV is built in superbuild
       else()
         list(APPEND _dir_candidates "${OpenCV_DIR}/bin") # External OpenCV build is used
       endif()
     endif()
   endif()
 
+  if(MITK_USE_OpenMesh)
+    if(WIN32)
+      list(APPEND _dir_candidates "${MITK_EXTERNAL_PROJECT_PREFIX}")
+    endif()
+  endif()
+
   if(MITK_USE_Python3)
     list(APPEND _dir_candidates "${CTK_DIR}/CMakeExternals/Install/bin")
     get_filename_component(_python_dir "${Python3_EXECUTABLE}" DIRECTORY)
     list(APPEND _dir_candidates "${_python_dir}")
   endif()
 
   if(MITK_USE_TOF_PMDO3 OR MITK_USE_TOF_PMDCAMCUBE OR MITK_USE_TOF_PMDCAMBOARD)
     list(APPEND _dir_candidates "${MITK_PMD_SDK_DIR}/plugins" "${MITK_PMD_SDK_DIR}/bin")
   endif()
 
   if(MITK_USE_CTK)
     list(APPEND _dir_candidates "${CTK_LIBRARY_DIRS}")
     foreach(_ctk_library ${CTK_LIBRARIES})
       if(${_ctk_library}_LIBRARY_DIRS)
         list(APPEND _dir_candidates "${${_ctk_library}_LIBRARY_DIRS}")
       endif()
     endforeach()
   endif()
 
   if(MITK_USE_BLUEBERRY)
     if(DEFINED CTK_PLUGIN_RUNTIME_OUTPUT_DIRECTORY)
       if(IS_ABSOLUTE "${CTK_PLUGIN_RUNTIME_OUTPUT_DIRECTORY}")
         list(APPEND _dir_candidates "${CTK_PLUGIN_RUNTIME_OUTPUT_DIRECTORY}")
       else()
         list(APPEND _dir_candidates "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CTK_PLUGIN_RUNTIME_OUTPUT_DIRECTORY}")
       endif()
     endif()
   endif()
 
   if(MITK_LIBRARY_DIRS)
     list(APPEND _dir_candidates ${MITK_LIBRARY_DIRS})
   endif()
 
   ###################################################################
   #get the search paths added by the mitkFunctionAddLibrarySearchPath
   file(GLOB _additional_path_info_files "${MITK_SUPERBUILD_BINARY_DIR}/MITK-AdditionalLibPaths/*.cmake")
 
   foreach(_additional_path_info_file ${_additional_path_info_files})
     get_filename_component(_additional_info_name ${_additional_path_info_file} NAME_WE)
     include(${_additional_path_info_file})
     if(GLS_DEBUG)
       list(APPEND _dir_candidates ${${_additional_info_name}_ADDITIONAL_DEBUG_LIBRARY_SEARCH_PATHS})
     elseif(GLS_MINSIZEREL)
       list(APPEND _dir_candidates ${${_additional_info_name}_ADDITIONAL_MINSIZEREL_LIBRARY_SEARCH_PATHS})
     elseif(GLS_RELWITHDEBINFO)
       list(APPEND _dir_candidates ${${_additional_info_name}_ADDITIONAL_RELWITHDEBINFO_LIBRARY_SEARCH_PATHS})
     else() #Release
       list(APPEND _dir_candidates ${${_additional_info_name}_ADDITIONAL_RELEASE_LIBRARY_SEARCH_PATHS})
     endif()
   endforeach(_additional_path_info_file ${_additional_path_info_files})
 
 
   ###############################################
   #sanitize all candidates and compile final list
   list(REMOVE_DUPLICATES _dir_candidates)
 
   set(_search_dirs )
   foreach(_dir ${_dir_candidates})
     if(EXISTS "${_dir}/${intermediate_dir}")
       list(APPEND _search_dirs "${_dir}/${intermediate_dir}")
     else()
       list(APPEND _search_dirs "${_dir}")
     endif()
   endforeach()
 
   # Special handling for "internal" search dirs. The intermediate directory
   # might not have been created yet, so we can't check for its existence.
   # Hence we just add it for Windows without checking.
   set(_internal_search_dirs "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/plugins")
   if(WIN32)
     foreach(_dir ${_internal_search_dirs})
       set(_search_dirs "${_dir}/${intermediate_dir}" ${_search_dirs})
     endforeach()
   else()
     set(_search_dirs ${_internal_search_dirs} ${_search_dirs})
   endif()
   list(REMOVE_DUPLICATES _search_dirs)
 
   set(${search_path} ${_search_dirs} PARENT_SCOPE)
 endfunction()
diff --git a/CMakeExternals/OpenMesh.cmake b/CMakeExternals/OpenMesh.cmake
index f0636d75c5..4e02342298 100644
--- a/CMakeExternals/OpenMesh.cmake
+++ b/CMakeExternals/OpenMesh.cmake
@@ -1,50 +1,50 @@
 #-----------------------------------------------------------------------------
 # OpenMesh
 #-----------------------------------------------------------------------------
 
 if(MITK_USE_OpenMesh)
   # Sanity checks
   if(DEFINED OpenMesh_DIR AND NOT EXISTS "${OpenMesh_DIR}")
     message(FATAL_ERROR "OpenMesh_DIR variable is defined but corresponds to non-existing directory")
   endif()
 
   set(proj OpenMesh)
   set(proj_DEPENDENCIES )
   set(OpenMesh_DEPENDS ${proj})
 
   if(NOT DEFINED OpenMesh_DIR)
 
     set(additional_args )
 
     if(CTEST_USE_LAUNCHERS)
       list(APPEND additional_args
         "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake"
       )
     endif()
 
     ExternalProject_Add(${proj}
       LIST_SEPARATOR ${sep}
       URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/OpenMesh-8.1.tar.gz
       URL_MD5 9e1eb6feeca3882ab95f9fc97681a4da
       CMAKE_GENERATOR ${gen}
       CMAKE_GENERATOR_PLATFORM ${gen_platform}
       CMAKE_ARGS
         ${ep_common_args}
         ${additional_args}
+      CMAKE_CACHE_ARGS
+        ${ep_common_cache_args}
         -DBUILD_APPS:BOOL=OFF
         -DOPENMESH_BUILD_SHARED:BOOL=ON
         -DOPENMESH_DOCS:BOOL=OFF
-      CMAKE_CACHE_ARGS
-        ${ep_common_cache_args}
       CMAKE_CACHE_DEFAULT_ARGS
         ${ep_common_cache_default_args}
       DEPENDS ${proj_DEPENDENCIES}
     )
 
     set(OpenMesh_DIR "${ep_prefix}")
     mitkFunctionInstallExternalCMakeProject(${proj})
 
   else()
     mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}")
   endif()
 endif()
diff --git a/Modules/Core/src/IO/mitkSurfaceStlIO.cpp b/Modules/Core/src/IO/mitkSurfaceStlIO.cpp
index e64e9a1634..21aeb441e0 100644
--- a/Modules/Core/src/IO/mitkSurfaceStlIO.cpp
+++ b/Modules/Core/src/IO/mitkSurfaceStlIO.cpp
@@ -1,160 +1,160 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkSurfaceStlIO.h"
 
 #include "mitkIOMimeTypes.h"
 #include "mitkLocaleSwitch.h"
 #include "mitkSurface.h"
 
 #include <vtkCleanPolyData.h>
 #include <vtkErrorCode.h>
 #include <vtkPolyDataNormals.h>
 #include <vtkSTLReader.h>
 #include <vtkSTLWriter.h>
 #include <vtkSmartPointer.h>
 #include <vtkTriangleFilter.h>
 
 namespace mitk
 {
-  std::string SurfaceStlIO::OPTION_MERGE_POINTS()
-  {
-    static std::string s = "Merge points";
-    return s;
-  }
+  // std::string SurfaceStlIO::OPTION_MERGE_POINTS()
+  // {
+  //   static std::string s = "Merge points";
+  //   return s;
+  // }
 
   std::string SurfaceStlIO::OPTION_TAG_SOLIDS()
   {
     static std::string s = "Tag solids";
     return s;
   }
 
   std::string SurfaceStlIO::OPTION_CLEAN()
   {
     static std::string s = "Clean poly data";
     return s;
   }
 
   SurfaceStlIO::SurfaceStlIO()
     : SurfaceVtkIO(Surface::GetStaticNameOfClass(), IOMimeTypes::STEREOLITHOGRAPHY_MIMETYPE(), "Stereolithography")
   {
     Options defaultOptions;
 
-    defaultOptions[OPTION_MERGE_POINTS()] = us::Any(true);
+    // defaultOptions[OPTION_MERGE_POINTS()] = us::Any(true);
     defaultOptions[OPTION_TAG_SOLIDS()] = us::Any(false);
     defaultOptions[OPTION_CLEAN()] = us::Any(true);
 
     this->SetDefaultReaderOptions(defaultOptions);
 
     this->RegisterService();
   }
 
   std::vector<itk::SmartPointer<BaseData>> SurfaceStlIO::DoRead()
   {
     LocaleSwitch localeSwitch("C");
 
     Options options = this->GetReaderOptions();
 
     mitk::Surface::Pointer output = mitk::Surface::New();
 
     vtkSmartPointer<vtkSTLReader> stlReader = vtkSmartPointer<vtkSTLReader>::New();
     stlReader->SetFileName(this->GetLocalFileName().c_str());
 
-    bool mergePoints = true;
+    // bool mergePoints = true;
     bool tagSolids = false;
     bool cleanData = true;
 
     try
     {
-      mergePoints = us::any_cast<bool>(options[OPTION_MERGE_POINTS()]);
+      // mergePoints = us::any_cast<bool>(options[OPTION_MERGE_POINTS()]);
       tagSolids = us::any_cast<bool>(options[OPTION_TAG_SOLIDS()]);
       cleanData = us::any_cast<bool>(options[OPTION_CLEAN()]);
     }
     catch (const us::BadAnyCastException &e)
     {
       MITK_WARN << "Unexpected error: " << e.what();
     }
 
-    stlReader->SetMerging(mergePoints);
+    // stlReader->SetMerging(mergePoints);
     stlReader->SetScalarTags(tagSolids);
 
     vtkSmartPointer<vtkPolyDataNormals> normalsGenerator = vtkSmartPointer<vtkPolyDataNormals>::New();
     normalsGenerator->SetInputConnection(stlReader->GetOutputPort());
 
     vtkSmartPointer<vtkPolyDataAlgorithm> algo = normalsGenerator;
     if (cleanData)
     {
       vtkSmartPointer<vtkCleanPolyData> cleanPolyDataFilter = vtkSmartPointer<vtkCleanPolyData>::New();
       cleanPolyDataFilter->SetInputConnection(normalsGenerator->GetOutputPort());
       cleanPolyDataFilter->PieceInvariantOff();
       cleanPolyDataFilter->ConvertLinesToPointsOff();
       cleanPolyDataFilter->ConvertPolysToLinesOff();
       cleanPolyDataFilter->ConvertStripsToPolysOff();
-      if (mergePoints)
-      {
+      // if (mergePoints)
+      // {
         cleanPolyDataFilter->PointMergingOn();
-      }
+      // }
       algo = cleanPolyDataFilter;
     }
     algo->Update();
 
     if (algo->GetOutput() != nullptr)
     {
       vtkSmartPointer<vtkPolyData> surfaceWithNormals = algo->GetOutput();
       output->SetVtkPolyData(surfaceWithNormals);
     }
 
     std::vector<BaseData::Pointer> result;
     result.push_back(output.GetPointer());
     return result;
   }
 
   void SurfaceStlIO::Write()
   {
     LocaleSwitch localeSwitch("C");
 
     ValidateOutputLocation();
     const auto *input = dynamic_cast<const Surface *>(this->GetInput());
 
     const unsigned int timesteps = input->GetTimeGeometry()->CountTimeSteps();
     for (unsigned int t = 0; t < timesteps; ++t)
     {
       std::string fileName;
       vtkSmartPointer<vtkPolyData> polyData = this->GetPolyData(t, fileName);
       vtkSmartPointer<vtkTriangleFilter> triangleFilter = vtkSmartPointer<vtkTriangleFilter>::New();
       triangleFilter->SetInputData(polyData);
       vtkSmartPointer<vtkSTLWriter> writer = vtkSmartPointer<vtkSTLWriter>::New();
       writer->SetInputConnection(triangleFilter->GetOutputPort());
 
       // The vtk stl writer cannot write to streams
       LocalFile localFile(this);
       writer->SetFileName(localFile.GetFileName().c_str());
 
       if (writer->Write() == 0 || writer->GetErrorCode() != 0)
       {
         mitkThrow() << "Error during surface writing"
                     << (writer->GetErrorCode() ?
                           std::string(": ") + vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) :
                           std::string());
       }
 
       if (this->GetOutputStream() && input->GetTimeGeometry()->CountTimeSteps() > 1)
       {
         MITK_WARN << "Writing multiple time-steps to output streams is not supported. "
                   << "Only the first time-step will be written";
         break;
       }
     }
   }
 
   SurfaceStlIO *SurfaceStlIO::IOClone() const { return new SurfaceStlIO(*this); }
 }
diff --git a/Modules/Core/src/IO/mitkSurfaceStlIO.h b/Modules/Core/src/IO/mitkSurfaceStlIO.h
index ffe36e1e60..2e95e8ab2e 100644
--- a/Modules/Core/src/IO/mitkSurfaceStlIO.h
+++ b/Modules/Core/src/IO/mitkSurfaceStlIO.h
@@ -1,45 +1,47 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef _MITK_SURFACE_STL_IO_H_
 #define _MITK_SURFACE_STL_IO_H_
 
 #include "mitkSurfaceVtkIO.h"
 
 namespace mitk
 {
   class SurfaceStlIO : public mitk::SurfaceVtkIO
   {
   public:
     SurfaceStlIO();
 
     // -------------- AbstractFileReader -------------
 
     using AbstractFileReader::Read;
 
     // -------------- AbstractFileWriter -------------
 
     void Write() override;
 
   protected:
     std::vector<itk::SmartPointer<BaseData>> DoRead() override;
 
   private:
     SurfaceStlIO *IOClone() const override;
 
-    static std::string OPTION_MERGE_POINTS();
+    // vtkSTLReader crashes with this option
+    // static std::string OPTION_MERGE_POINTS();
+
     static std::string OPTION_TAG_SOLIDS();
     static std::string OPTION_CLEAN();
   };
 }
 
 #endif //_MITK_SURFACE_STL_IO_H_
diff --git a/Modules/Remeshing/CMakeLists.txt b/Modules/Remeshing/CMakeLists.txt
index 3e62ed8d2f..2697e18603 100644
--- a/Modules/Remeshing/CMakeLists.txt
+++ b/Modules/Remeshing/CMakeLists.txt
@@ -1,4 +1,9 @@
 mitk_create_module(
   DEPENDS MitkCore
-  PACKAGE_DEPENDS OpenMesh|OpenMeshCore
+  PACKAGE_DEPENDS OpenMesh|OpenMeshTools
 )
+
+if(TARGET ${MODULE_TARGET})
+  target_link_libraries(${MODULE_TARGET} PRIVATE OpenMeshTools)
+  target_compile_definitions(${MODULE_TARGET} PRIVATE -D_USE_MATH_DEFINES)
+endif()
diff --git a/Modules/Remeshing/include/mitkRemeshing.h b/Modules/Remeshing/include/mitkRemeshing.h
index 74336d8e14..ef068d6424 100644
--- a/Modules/Remeshing/include/mitkRemeshing.h
+++ b/Modules/Remeshing/include/mitkRemeshing.h
@@ -1,27 +1,39 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef mitkRemeshing_h
 #define mitkRemeshing_h
 
 #include <mitkSurface.h>
 #include <MitkRemeshingExports.h>
 
 namespace mitk
 {
   namespace Remeshing
   {
-    MITKREMESHING_EXPORT Surface::Pointer Decimate(Surface::ConstPointer input);
+    /** \brief Reduce the number of vertices of an mitk::Surface.
+      *
+      * The decimation is applied separately to all time steps of the input surface.
+      * The meshes of the resulting surface are guaranteed to consist of triangles only.
+      *
+      * \param[in] input Input surface
+      * \param[in] percent Relative number of vertices after decimation [0, 1]
+      * \param[in] calculateNormals Calculate normals after decimation (\c true by default)
+      * \param[in] flipNormals Flip calculated normals (\c false by default)
+      *
+      * \return Decimated surface
+      */
+    MITKREMESHING_EXPORT Surface::Pointer Decimate(const Surface* input, double percent, bool calculateNormals = true, bool flipNormals = false);
   }
 }
 
 #endif
diff --git a/Modules/Remeshing/src/mitkRemeshing.cpp b/Modules/Remeshing/src/mitkRemeshing.cpp
index 915be3ece4..dd531cfaaf 100644
--- a/Modules/Remeshing/src/mitkRemeshing.cpp
+++ b/Modules/Remeshing/src/mitkRemeshing.cpp
@@ -1,18 +1,185 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include <mitkRemeshing.h>
 
-mitk::Surface::Pointer mitk::Remeshing::Decimate(Surface::ConstPointer input)
+#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
+#include <OpenMesh/Tools/Decimater/DecimaterT.hh>
+#include <OpenMesh/Tools/Decimater/ModQuadricT.hh>
+
+#include <vtkCellArray.h>
+#include <vtkIdList.h>
+#include <vtkPoints.h>
+#include <vtkPolyData.h>
+#include <vtkPolyDataNormals.h>
+#include <vtkSmartPointer.h>
+#include <vtkTriangleFilter.h>
+
+#include <algorithm>
+#include <functional>
+
+using Mesh = OpenMesh::TriMesh_ArrayKernelT<OpenMesh::DefaultTraitsDouble>;
+
+namespace
 {
-  return input->Clone();
+  bool IsValidPolyData(vtkPolyData* polyData)
+  {
+    return nullptr != polyData && 0 < polyData->GetNumberOfPoints() &&
+      (0 < polyData->GetNumberOfPolys() || 0 < polyData->GetNumberOfStrips());
+  }
+
+  vtkSmartPointer<vtkPolyData> TriangulatePolyData(vtkPolyData* polyData)
+  {
+    auto triangleFilter = vtkSmartPointer<vtkTriangleFilter>::New();
+    triangleFilter->SetInputData(polyData);
+    triangleFilter->PassVertsOff();
+    triangleFilter->PassLinesOff();
+
+    triangleFilter->Update();
+
+    return triangleFilter->GetOutput();
+  }
+
+  vtkSmartPointer<vtkPolyData> CalculateNormals(vtkPolyData* polyData, bool flipNormals)
+  {
+    auto polyDataNormals = vtkSmartPointer<vtkPolyDataNormals>::New();
+    polyDataNormals->SetInputData(polyData);
+
+    if (flipNormals)
+      polyDataNormals->FlipNormalsOn();
+
+    polyDataNormals->Update();
+
+    return polyDataNormals->GetOutput();
+  }
+
+  Mesh ConvertPolyDataToMesh(vtkPolyData* polyData)
+  {
+    Mesh mesh;
+
+    auto* points = polyData->GetPoints();
+    const auto numPoints = points->GetNumberOfPoints();
+
+    for (std::remove_const_t<decltype(numPoints)> i = 0; i < numPoints; ++i)
+      mesh.add_vertex(Mesh::Point(points->GetPoint(i)));
+
+    auto* polys = polyData->GetPolys();
+    const auto numPolys = polys->GetNumberOfCells();
+
+    auto ids = vtkSmartPointer<vtkIdList>::New();
+    std::array<Mesh::VertexHandle, 3> vertexHandles;
+
+    for (std::remove_const_t<decltype(numPolys)> i = 0; i < numPolys; ++i)
+    {
+      polys->GetCellAtId(i, ids);
+
+      vertexHandles[0] = Mesh::VertexHandle(static_cast<int>(ids->GetId(0)));
+      vertexHandles[1] = Mesh::VertexHandle(static_cast<int>(ids->GetId(1)));
+      vertexHandles[2] = Mesh::VertexHandle(static_cast<int>(ids->GetId(2)));
+
+      mesh.add_face(vertexHandles.data(), 3);
+    }
+
+    return mesh;
+  }
+
+  vtkSmartPointer<vtkPolyData> ConvertMeshToPolyData(const Mesh& mesh)
+  {
+    auto polyData = vtkSmartPointer<vtkPolyData>::New();
+
+    const auto numVertices = mesh.n_vertices();
+    auto points = vtkSmartPointer<vtkPoints>::New();
+    points->SetNumberOfPoints(numVertices);
+
+    for (std::remove_const_t<decltype(numVertices)> i = 0; i < numVertices; ++i)
+      points->SetPoint(i, mesh.point(Mesh::VertexHandle(static_cast<int>(i))).data());
+
+    polyData->SetPoints(points);
+
+    const auto numFaces = mesh.n_faces();
+    auto polys = vtkSmartPointer<vtkCellArray>::New();
+
+    auto ids = vtkSmartPointer<vtkIdList>::New();
+    ids->SetNumberOfIds(3);
+    Mesh::CFVIter iter;
+
+    for (std::remove_const_t<decltype(numFaces)> i = 0; i < numFaces; ++i)
+    {
+      iter = mesh.cfv_iter(Mesh::FaceHandle(static_cast<int>(i)));
+
+      ids->SetId(0, (iter++)->idx());
+      ids->SetId(1, (iter++)->idx());
+      ids->SetId(2, iter->idx());
+
+      polys->InsertNextCell(ids);
+    }
+
+    polyData->SetPolys(polys);
+
+    return polyData;
+  }
+
+  mitk::Surface::Pointer ProcessEachTimeStep(const mitk::Surface* input, bool calculateNormals, bool flipNormals, const std::function<void(Mesh&)>& ProcessMesh)
+  {
+    if (nullptr == input || !input->IsInitialized())
+      return nullptr;
+
+    auto output = mitk::Surface::New();
+    const auto numTimeSteps = input->GetTimeSteps();
+
+    for (std::remove_const_t<decltype(numTimeSteps)> t = 0; t < numTimeSteps; ++t)
+    {
+      vtkSmartPointer<vtkPolyData> polyData = input->GetVtkPolyData(t);
+
+      if (IsValidPolyData(polyData))
+      {
+        polyData = TriangulatePolyData(polyData);
+
+        if (IsValidPolyData(polyData))
+        {
+          auto mesh = ConvertPolyDataToMesh(polyData);
+          ProcessMesh(mesh);
+          polyData = ConvertMeshToPolyData(mesh);
+
+          if (calculateNormals)
+            polyData = CalculateNormals(polyData, flipNormals);
+
+          output->SetVtkPolyData(polyData, t);
+          continue;
+        }
+      }
+
+      output->SetVtkPolyData(nullptr, t);
+    }
+
+    return output;
+  }
+}
+
+mitk::Surface::Pointer mitk::Remeshing::Decimate(const Surface* input, double percent, bool calculateNormals, bool flipNormals)
+{
+  return ProcessEachTimeStep(input, calculateNormals, flipNormals, [percent](Mesh& mesh) {
+    using Decimater = OpenMesh::Decimater::DecimaterT<Mesh>;
+    using HModQuadric = OpenMesh::Decimater::ModQuadricT<Mesh>::Handle;
+
+    Decimater decimater(mesh);
+
+    HModQuadric hModQuadric;
+    decimater.add(hModQuadric);
+    decimater.module(hModQuadric).unset_max_err();
+
+    decimater.initialize();
+    decimater.decimate_to(mesh.n_vertices() * std::max(0.0, std::min(percent, 1.0)));
+
+    mesh.garbage_collection();
+  });
 }
diff --git a/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.cpp b/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.cpp
index 7f084beb61..9ed0a2ef58 100644
--- a/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.cpp
+++ b/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.cpp
@@ -1,110 +1,123 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "QmitkRemeshingView.h"
 
 #include <ui_QmitkRemeshingViewControls.h>
 
 #include <berryQtStyleManager.h>
 
 #include <mitkNodePredicateAnd.h>
 #include <mitkNodePredicateDataType.h>
 #include <mitkNodePredicateNot.h>
 #include <mitkNodePredicateOr.h>
 #include <mitkNodePredicateProperty.h>
 #include <mitkRemeshing.h>
 
 const std::string QmitkRemeshingView::VIEW_ID = "org.mitk.views.remeshing";
 
 QmitkRemeshingView::QmitkRemeshingView()
   : m_Controls(new Ui::QmitkRemeshingViewControls)
 {
 }
 
 QmitkRemeshingView::~QmitkRemeshingView()
 {
 }
 
 void QmitkRemeshingView::CreateQtPartControl(QWidget* parent)
 {
   m_Controls->setupUi(parent);
 
   m_Controls->decimatePushButton->setIcon(berry::QtStyleManager::ThemeIcon(QStringLiteral(":/Remeshing/RemeshingIcon.svg")));
 
   m_Controls->selectionWidget->SetDataStorage(this->GetDataStorage());
   m_Controls->selectionWidget->SetSelectionIsOptional(true);
   m_Controls->selectionWidget->SetEmptyInfo(QStringLiteral("Select a surface"));
   m_Controls->selectionWidget->SetAutoSelectNewNodes(true);
   m_Controls->selectionWidget->SetNodePredicate(mitk::NodePredicateAnd::New(
     mitk::TNodePredicateDataType<mitk::Surface>::New(),
     mitk::NodePredicateNot::New(mitk::NodePredicateOr::New(
       mitk::NodePredicateProperty::New("helper object"),
       mitk::NodePredicateProperty::New("hidden object")))));
 
   connect(m_Controls->selectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkRemeshingView::OnSurfaceChanged);
   connect(m_Controls->polygonCountSlider, SIGNAL(valueChanged(int)), this, SLOT(OnPolygonCountChanged(int)));
   connect(m_Controls->polygonCountSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnPolygonCountChanged(int)));
+  connect(m_Controls->calculateNormalsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnCalculateNormalsChanged(int)));
   connect(m_Controls->decimatePushButton, SIGNAL(clicked()), this, SLOT(OnDecimateButtonClicked()));
 
   this->OnSurfaceChanged(m_Controls->selectionWidget->GetSelectedNodes());
 }
 
 void QmitkRemeshingView::OnSurfaceChanged(const QmitkSingleNodeSelectionWidget::NodeList& nodes)
 {
   this->EnableWidgets(!nodes.empty() && nodes.front().IsNotNull());
 }
 
 void QmitkRemeshingView::OnPolygonCountChanged(int polygonCount)
 {
   if (polygonCount != m_Controls->polygonCountSlider->value())
     m_Controls->polygonCountSlider->setValue(polygonCount);
 
   if (polygonCount != m_Controls->polygonCountSpinBox->value())
     m_Controls->polygonCountSpinBox->setValue(polygonCount);
 }
 
+void QmitkRemeshingView::OnCalculateNormalsChanged(int checkState)
+{
+  m_Controls->flipNormalsCheckBox->setEnabled(Qt::Unchecked != checkState);
+}
+
 void QmitkRemeshingView::OnDecimateButtonClicked()
 {
   mitk::DataNode::Pointer selectedNode = m_Controls->selectionWidget->GetSelectedNode();
   mitk::Surface::ConstPointer input = static_cast<mitk::Surface*>(selectedNode->GetData());
   mitk::Surface::Pointer output;
 
   try
   {
-    output = mitk::Remeshing::Decimate(input);
+    output = mitk::Remeshing::Decimate(input,
+      0.01 * m_Controls->polygonCountSpinBox->value(),
+      m_Controls->calculateNormalsCheckBox->isChecked(),
+      m_Controls->flipNormalsCheckBox->isChecked());
   }
   catch(const mitk::Exception& exception)
   {
     MITK_ERROR << exception.GetDescription();
     return;
   }
 
+  if (output.IsNull())
+    return;
+
   auto newNode = mitk::DataNode::New();
-  newNode->SetName(QString("%1 (%2%)").arg(selectedNode->GetName().c_str()).arg(m_Controls->polygonCountSpinBox->value()).toStdString());
+  newNode->SetName(QString("%1 (decimated)").arg(selectedNode->GetName().c_str()).toStdString());
   newNode->SetData(output);
 
   this->GetDataStorage()->Add(newNode, selectedNode);
 }
 
 void QmitkRemeshingView::EnableWidgets(bool enable)
 {
   m_Controls->polygonCountSlider->setEnabled(enable);
   m_Controls->polygonCountSpinBox->setEnabled(enable);
-  m_Controls->polygonDistributionComboBox->setEnabled(enable);
+  m_Controls->calculateNormalsCheckBox->setEnabled(enable);
+  m_Controls->flipNormalsCheckBox->setEnabled(enable && m_Controls->calculateNormalsCheckBox->isChecked());
   m_Controls->decimatePushButton->setEnabled(enable);
 }
 
 void QmitkRemeshingView::SetFocus()
 {
   m_Controls->selectionWidget->setFocus();
 }
 
diff --git a/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.h b/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.h
index a2cc446cd7..679f1679ec 100644
--- a/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.h
+++ b/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingView.h
@@ -1,48 +1,49 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #ifndef QmitkRemeshingView_h
 #define QmitkRemeshingView_h
 
 #include <QmitkAbstractView.h>
 #include <QmitkSingleNodeSelectionWidget.h>
 
 namespace Ui
 {
   class QmitkRemeshingViewControls;
 }
 
 class QmitkRemeshingView : public QmitkAbstractView
 {
   Q_OBJECT
 
 public:
   static const std::string VIEW_ID;
 
   QmitkRemeshingView();
   ~QmitkRemeshingView() override;
 
   void CreateQtPartControl(QWidget* parent) override;
   void SetFocus() override;
 
 private slots:
   void OnSurfaceChanged(const QmitkSingleNodeSelectionWidget::NodeList& nodes);
   void OnPolygonCountChanged(int polygonCount);
+  void OnCalculateNormalsChanged(int checkState);
   void OnDecimateButtonClicked();
 
 private:
   void EnableWidgets(bool enable);
 
   Ui::QmitkRemeshingViewControls* m_Controls;
 };
 
 #endif
diff --git a/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingViewControls.ui b/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingViewControls.ui
index bca912609c..d06d2ff7fc 100644
--- a/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingViewControls.ui
+++ b/Plugins/org.mitk.gui.qt.remeshing/src/internal/QmitkRemeshingViewControls.ui
@@ -1,182 +1,172 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <ui version="4.0">
  <class>QmitkRemeshingViewControls</class>
  <widget class="QWidget" name="QmitkRemeshingViewControls">
   <property name="enabled">
    <bool>true</bool>
   </property>
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>253</width>
     <height>573</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>Remeshing</string>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="surfaceLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>Surface</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="1" colspan="2">
+      <widget class="QCheckBox" name="flipNormalsCheckBox">
+       <property name="text">
+        <string>Flip normals</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="polygonCountLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>Polygon count</string>
+       </property>
+      </widget>
+     </item>
      <item row="1" column="1">
       <widget class="QSlider" name="polygonCountSlider">
        <property name="sizePolicy">
         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
          <horstretch>0</horstretch>
          <verstretch>0</verstretch>
         </sizepolicy>
        </property>
        <property name="minimum">
         <number>1</number>
        </property>
        <property name="maximum">
         <number>100</number>
        </property>
        <property name="pageStep">
         <number>10</number>
        </property>
        <property name="value">
-        <number>100</number>
+        <number>50</number>
        </property>
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
       </widget>
      </item>
+     <item row="0" column="1" colspan="2">
+      <widget class="QmitkSingleNodeSelectionWidget" name="selectionWidget" native="true">
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>40</height>
+        </size>
+       </property>
+      </widget>
+     </item>
      <item row="1" column="2">
       <widget class="QSpinBox" name="polygonCountSpinBox">
        <property name="suffix">
         <string>%</string>
        </property>
        <property name="minimum">
         <number>1</number>
        </property>
        <property name="maximum">
         <number>100</number>
        </property>
        <property name="singleStep">
         <number>1</number>
        </property>
        <property name="value">
-        <number>100</number>
+        <number>50</number>
        </property>
       </widget>
      </item>
-     <item row="2" column="0">
-      <widget class="QLabel" name="polygonDistributionLabel">
-       <property name="sizePolicy">
-        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-         <horstretch>0</horstretch>
-         <verstretch>0</verstretch>
-        </sizepolicy>
-       </property>
+     <item row="2" column="1" colspan="2">
+      <widget class="QCheckBox" name="calculateNormalsCheckBox">
        <property name="text">
-        <string>Polygon distribution</string>
-       </property>
-      </widget>
-     </item>
-     <item row="0" column="0">
-      <widget class="QLabel" name="surfaceLabel">
-       <property name="sizePolicy">
-        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-         <horstretch>0</horstretch>
-         <verstretch>0</verstretch>
-        </sizepolicy>
+        <string>Calculate normals</string>
        </property>
-       <property name="text">
-        <string>Surface</string>
+       <property name="checked">
+        <bool>true</bool>
        </property>
       </widget>
      </item>
-     <item row="0" column="1" colspan="2">
-      <widget class="QmitkSingleNodeSelectionWidget" name="selectionWidget" native="true">
-       <property name="minimumSize">
-        <size>
-         <width>0</width>
-         <height>40</height>
-        </size>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="0">
-      <widget class="QLabel" name="polygonCountLabel">
-       <property name="sizePolicy">
-        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-         <horstretch>0</horstretch>
-         <verstretch>0</verstretch>
-        </sizepolicy>
-       </property>
-       <property name="text">
-        <string>Polygon count</string>
-       </property>
-      </widget>
-     </item>
-     <item row="2" column="1" colspan="2">
-      <widget class="QComboBox" name="polygonDistributionComboBox">
-       <item>
-        <property name="text">
-         <string>Adaptive</string>
-        </property>
-       </item>
-       <item>
-        <property name="text">
-         <string>Uniform</string>
-        </property>
-       </item>
-      </widget>
-     </item>
     </layout>
    </item>
    <item>
     <widget class="QPushButton" name="decimatePushButton">
      <property name="enabled">
       <bool>true</bool>
      </property>
      <property name="text">
       <string>Decimate</string>
      </property>
      <property name="icon">
       <iconset resource="../../resources/Remeshing.qrc">
        <normaloff>:/Remeshing/RemeshingIcon.svg</normaloff>:/Remeshing/RemeshingIcon.svg</iconset>
      </property>
      <property name="iconSize">
       <size>
        <width>24</width>
        <height>24</height>
       </size>
      </property>
     </widget>
    </item>
    <item>
     <spacer name="verticalSpacer">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
      </property>
      <property name="sizeHint" stdset="0">
       <size>
        <width>20</width>
        <height>40</height>
       </size>
      </property>
     </spacer>
    </item>
   </layout>
  </widget>
  <customwidgets>
   <customwidget>
    <class>QmitkSingleNodeSelectionWidget</class>
    <extends>QWidget</extends>
    <header location="global">QmitkSingleNodeSelectionWidget.h</header>
    <container>1</container>
   </customwidget>
  </customwidgets>
  <tabstops>
   <tabstop>polygonCountSpinBox</tabstop>
   <tabstop>decimatePushButton</tabstop>
  </tabstops>
  <resources>
   <include location="../../resources/Remeshing.qrc"/>
  </resources>
  <connections/>
 </ui>