diff --git a/CMakeLists.txt b/CMakeLists.txt index eadbb1d9f..3d286bc29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,7 @@ cmake_dependent_option(WITH_FREETYPEFONT "Build FreeTypeFont plugin" OFF "NOT WI option(WITH_HARFBUZZFONT "Build HarfBuzzFont plugin" OFF) option(WITH_JPEGIMAGECONVERTER "Build JpegImageConverter plugin" OFF) option(WITH_JPEGIMPORTER "Build JpegImporter plugin" OFF) +option(WITH_MESHOPTIMIZERSCENECONVERTER "Build MeshOptimizerSceneConverter plugin" OFF) option(WITH_MINIEXRIMAGECONVERTER "Build MiniExrImageConverter plugin" OFF) cmake_dependent_option(WITH_OPENDDL "Build OpenDdl library" OFF "NOT WITH_OPENGEXIMPORTER" ON) option(WITH_OPENGEXIMPORTER "Build OpenGexImporter plugin" OFF) diff --git a/doc/building-plugins.dox b/doc/building-plugins.dox index 18eff0ccc..eb5b18121 100644 --- a/doc/building-plugins.dox +++ b/doc/building-plugins.dox @@ -334,6 +334,9 @@ By default no plugins are built and you need to select them manually: @ref Trade::JpegImageConverter "JpegImageConverter" plugin. - `WITH_JPEGIMPORTER` --- Build the @ref Trade::JpegImporter "JpegImporter" plugin. Depends on [libJPEG](http://libjpeg.sourceforge.net/). +- `WITH_MESHOPTIMIZERSCENECONVERTER` --- Build the + @ref Trade::MeshOptimizerSceneConverter "MeshOptimizerSceneConverter" + plugin. - `WITH_MINIEXRIMAGECONVERTER` --- Build the @ref Trade::MiniExrImageConverter "MiniExrImageConverter" plugin. - `WITH_OPENGEXIMPORTER` --- Build the @ref Trade::OpenGexImporter "OpenGexImporter" diff --git a/doc/changelog-plugins.dox b/doc/changelog-plugins.dox index 105972272..ca9215f1a 100644 --- a/doc/changelog-plugins.dox +++ b/doc/changelog-plugins.dox @@ -64,6 +64,8 @@ namespace Magnum { writing binary PLY files - New @ref Trade::StlImporter "StlImporter" plugin for importing binary STL files +- New @ref Trade::MeshOptimizerSceneConverter "MeshOptimizerSceneConverter" + plugin, integrating [meshoptimizer](https://github.com/zeux/meshoptimizer) - Animated GIF support in @ref Trade::StbImageImporter "StbImageImporter" - Texture coordinate set import in @ref Trade::AssimpImporter "AssimpImporter" and @ref Trade::TinyGltfImporter "TinyGltfImporter" (see diff --git a/doc/cmake-plugins.dox b/doc/cmake-plugins.dox index bb3c0fab8..7cf0a4163 100644 --- a/doc/cmake-plugins.dox +++ b/doc/cmake-plugins.dox @@ -133,6 +133,9 @@ This command will not try to find any actual plugin. The plugins are: - `JpegImageConverter` --- @ref Trade::JpegImageConverter "JpegImageConverter" plugin - `JpegImporter` --- @ref Trade::JpegImporter "JpegImporter" plugin +- `MeshOptimizerSceneConverter` --- + @ref Trade::MeshOptimizerSceneConverter "MeshOptimizerSceneConverter" + plugin - `MiniExrImageConverter` --- @ref Trade::MiniExrImageConverter "MiniExrImageConverter" plugin - `OpenGexImporter` --- @ref Trade::OpenGexImporter "OpenGexImporter" plugin diff --git a/doc/custom-buildsystems-plugins-order.dot b/doc/custom-buildsystems-plugins-order.dot index c1946836c..e1bf10584 100644 --- a/doc/custom-buildsystems-plugins-order.dot +++ b/doc/custom-buildsystems-plugins-order.dot @@ -69,6 +69,8 @@ digraph "Magnum Plugins dependency order" { AssimpImporter -> AnyImageImporter FreeTypeFont -> MagnumText HarfBuzzFont -> FreeTypeFont + MeshOptimizerSceneConverter -> MagnumTrade + MeshOptimizerSceneConverter -> MagnumMeshTools OpenGexImporter -> MagnumTrade OpenGexImporter -> AnyImageImporter OpenGexImporter -> MagnumOpenDdl diff --git a/modules/FindMagnumPlugins.cmake b/modules/FindMagnumPlugins.cmake index 2635246de..34cf257fc 100644 --- a/modules/FindMagnumPlugins.cmake +++ b/modules/FindMagnumPlugins.cmake @@ -25,6 +25,7 @@ # HarfBuzzFont - HarfBuzz font # JpegImageConverter - JPEG image converter # JpegImporter - JPEG importer +# MeshOptimizerSceneConverter - MeshOptimizer scene converter # MiniExrImageConverter - OpenEXR image converter using miniexr # OpenGexImporter - OpenGEX importer # PngImageConverter - PNG image converter @@ -108,6 +109,8 @@ foreach(_component ${MagnumPlugins_FIND_COMPONENTS}) if(_component STREQUAL AssimpImporter) list(APPEND _MAGNUMPLUGINS_${_component}_MAGNUM_DEPENDENCIES AnyImageImporter) + elseif(_component STREQUAL MeshOptimizerSceneConverter) + list(APPEND _MAGNUMPLUGINS_${_component}_MAGNUM_DEPENDENCIES MeshTools) elseif(_component STREQUAL OpenGexImporter) list(APPEND _MAGNUMPLUGINS_${_component}_MAGNUM_DEPENDENCIES AnyImageImporter) elseif(_component STREQUAL PrimitiveImporter) @@ -136,9 +139,11 @@ set(_MAGNUMPLUGINS_PLUGIN_COMPONENT_LIST AssimpImporter BasisImageConverter BasisImporter DdsImporter DevIlImageImporter DrFlacAudioImporter DrMp3AudioImporter DrWavAudioImporter Faad2AudioImporter FreeTypeFont HarfBuzzFont - JpegImageConverter JpegImporter MiniExrImageConverter OpenGexImporter - PngImageConverter PngImporter PrimitiveImporter StanfordImporter - StanfordSceneConverter StbImageConverter StbImageImporter StbTrueTypeFont StbVorbisAudioImporter StlImporter TinyGltfImporter) + JpegImageConverter JpegImporter MeshOptimizerSceneConverter + MiniExrImageConverter OpenGexImporter PngImageConverter PngImporter + PrimitiveImporter StanfordImporter StanfordSceneConverter StbImageConverter + StbImageImporter StbTrueTypeFont StbVorbisAudioImporter StlImporter + TinyGltfImporter) # Inter-component dependencies set(_MAGNUMPLUGINS_HarfBuzzFont_DEPENDENCIES FreeTypeFont) @@ -369,6 +374,17 @@ foreach(_component ${MagnumPlugins_FIND_COMPONENTS}) INTERFACE_LINK_LIBRARIES ${JPEG_LIBRARIES}) endif() + # MeshOptimizerSceneConverter plugin dependencies + elseif(_component STREQUAL MeshOptimizerSceneConverter) + if(NOT TARGET meshoptimizer) + find_package(meshoptimizer REQUIRED CONFIG) + set_property(TARGET MagnumPlugins::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES meshoptimizer::meshoptimizer) + else() + set_property(TARGET MagnumPlugins::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES meshoptimizer) + endif() + # MiniExrImageConverter has no dependencies # No special setup for the OpenDdl library # OpenGexImporter has no dependencies diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index 6b324bb12..46f42df5e 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -6,7 +6,7 @@ pkgdesc="Plugins for the Magnum C++11/C++14 graphics engine" arch=('i686' 'x86_64') url="https://magnum.graphics" license=('MIT') -depends=('magnum' 'devil' 'faad2' 'freetype2' 'harfbuzz' 'libjpeg' 'libpng' 'assimp' 'basis-universal-src') +depends=('magnum' 'devil' 'faad2' 'freetype2' 'harfbuzz' 'libjpeg' 'libpng' 'assimp' 'basis-universal-src' 'meshoptimizer') makedepends=('cmake' 'ninja') options=(!strip) provides=('magnum-plugins-git') @@ -36,6 +36,7 @@ build() { -DWITH_HARFBUZZFONT=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ diff --git a/package/archlinux/PKGBUILD-android-arm64 b/package/archlinux/PKGBUILD-android-arm64 index c59b17e88..e94d81de3 100644 --- a/package/archlinux/PKGBUILD-android-arm64 +++ b/package/archlinux/PKGBUILD-android-arm64 @@ -41,6 +41,7 @@ build() { -DWITH_DRFLACAUDIOIMPORTER=OFF \ -DWITH_DRMP3AUDIOIMPORTER=OFF \ -DWITH_DRWAVAUDIOIMPORTER=OFF \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PRIMITIVEIMPORTER=ON \ diff --git a/package/archlinux/PKGBUILD-clang b/package/archlinux/PKGBUILD-clang index 4e70b65a9..366261398 100644 --- a/package/archlinux/PKGBUILD-clang +++ b/package/archlinux/PKGBUILD-clang @@ -6,7 +6,7 @@ pkgdesc="Plugins for the Magnum C++11/C++14 graphics engine (built with clang)" arch=('i686' 'x86_64') url="https://magnum.graphics" license=('MIT') -depends=('magnum' 'devil' 'faad2' 'freetype2' 'harfbuzz' 'libjpeg' 'libpng' 'assimp') +depends=('magnum' 'devil' 'faad2' 'freetype2' 'harfbuzz' 'libjpeg' 'libpng' 'assimp' 'meshoptimizer') makedepends=('cmake' 'clang' 'ninja' 'basis-universal-src') options=(!strip) provides=('magnum-plugins-git') @@ -48,6 +48,7 @@ build() { -DWITH_HARFBUZZFONT=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ diff --git a/package/archlinux/PKGBUILD-clang-addresssanitizer b/package/archlinux/PKGBUILD-clang-addresssanitizer index fbb5f8351..0ea406971 100644 --- a/package/archlinux/PKGBUILD-clang-addresssanitizer +++ b/package/archlinux/PKGBUILD-clang-addresssanitizer @@ -6,7 +6,7 @@ pkgdesc="Plugins for the Magnum C++11/C++14 graphics engine (clang-addresssaniti arch=('i686' 'x86_64') url="https://magnum.graphics" license=('MIT') -depends=('magnum' 'ninja' 'devil' 'faad2' 'freetype2' 'harfbuzz' 'libjpeg' 'libpng' 'assimp') +depends=('magnum' 'ninja' 'devil' 'faad2' 'freetype2' 'harfbuzz' 'libjpeg' 'libpng' 'assimp' 'meshoptimizer') makedepends=('cmake' 'clang' 'basis-universal-src') options=(!strip) provides=('magnum-plugins-git') @@ -38,6 +38,7 @@ build() { -DWITH_HARFBUZZFONT=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ diff --git a/package/archlinux/PKGBUILD-clang-threadsanitizer b/package/archlinux/PKGBUILD-clang-threadsanitizer index a5eaab279..6efe53f96 100644 --- a/package/archlinux/PKGBUILD-clang-threadsanitizer +++ b/package/archlinux/PKGBUILD-clang-threadsanitizer @@ -6,7 +6,7 @@ pkgdesc="Plugins for the Magnum C++11/C++14 graphics engine (clang-threadsanitiz arch=('i686' 'x86_64') url="https://magnum.graphics" license=('MIT') -depends=('magnum' 'ninja' 'devil' 'faad2' 'freetype2' 'harfbuzz' 'libjpeg' 'libpng' 'assimp') +depends=('magnum' 'ninja' 'devil' 'faad2' 'freetype2' 'harfbuzz' 'libjpeg' 'libpng' 'assimp' 'meshoptimizer') makedepends=('cmake' 'clang' 'basis-universal-src') options=(!strip) provides=('magnum-plugins-git') @@ -37,6 +37,7 @@ build() { -DWITH_FREETYPEFONT=ON \ -DWITH_HARFBUZZFONT=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ diff --git a/package/archlinux/PKGBUILD-coverage b/package/archlinux/PKGBUILD-coverage index 66c74573f..00010e45c 100644 --- a/package/archlinux/PKGBUILD-coverage +++ b/package/archlinux/PKGBUILD-coverage @@ -7,7 +7,7 @@ arch=('i686' 'x86_64') url="https://magnum.graphics" license=('MIT') depends=('magnum' 'devil' 'faad2' 'freetype2' 'harfbuzz' 'libjpeg' 'libpng') -makedepends=('cmake' 'ninja' 'lcov' 'basis-universal-src') +makedepends=('cmake' 'ninja' 'lcov' 'basis-universal-src' 'meshoptimizer') options=(!strip) provides=('magnum-plugins-git') @@ -41,6 +41,7 @@ build() { -DWITH_HARFBUZZFONT=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ diff --git a/package/archlinux/PKGBUILD-emscripten b/package/archlinux/PKGBUILD-emscripten index fdb620dea..1fb9f9b62 100644 --- a/package/archlinux/PKGBUILD-emscripten +++ b/package/archlinux/PKGBUILD-emscripten @@ -41,6 +41,7 @@ build() { -DWITH_DRMP3AUDIOIMPORTER=ON \ -DWITH_DRWAVAUDIOIMPORTER=ON \ -DWITH_FAAD2AUDIOIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PRIMITIVEIMPORTER=ON \ diff --git a/package/archlinux/PKGBUILD-emscripten-wasm b/package/archlinux/PKGBUILD-emscripten-wasm index 29e793b00..0832ce98c 100644 --- a/package/archlinux/PKGBUILD-emscripten-wasm +++ b/package/archlinux/PKGBUILD-emscripten-wasm @@ -40,6 +40,7 @@ build() { -DWITH_DRMP3AUDIOIMPORTER=ON \ -DWITH_DRWAVAUDIOIMPORTER=ON \ -DWITH_FAAD2AUDIOIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PRIMITIVEIMPORTER=ON \ diff --git a/package/archlinux/PKGBUILD-emscripten-wasm-webgl2 b/package/archlinux/PKGBUILD-emscripten-wasm-webgl2 index 4b7f2501a..f14cdf2f1 100644 --- a/package/archlinux/PKGBUILD-emscripten-wasm-webgl2 +++ b/package/archlinux/PKGBUILD-emscripten-wasm-webgl2 @@ -40,6 +40,7 @@ build() { -DWITH_DRMP3AUDIOIMPORTER=ON \ -DWITH_DRWAVAUDIOIMPORTER=ON \ -DWITH_FAAD2AUDIOIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PRIMITIVEIMPORTER=ON \ diff --git a/package/archlinux/PKGBUILD-gcc48 b/package/archlinux/PKGBUILD-gcc48 index bffd9c68a..15b277236 100644 --- a/package/archlinux/PKGBUILD-gcc48 +++ b/package/archlinux/PKGBUILD-gcc48 @@ -7,7 +7,7 @@ arch=('i686' 'x86_64') url="https://magnum.graphics" license=('MIT') depends=('magnum' 'devil' 'faad2' 'freetype2' 'harfbuzz' 'libjpeg' 'libpng' 'assimp') -makedepends=('cmake' 'ninja' 'gcc48' 'basis-universal-src') +makedepends=('cmake' 'ninja' 'gcc48' 'basis-universal-src' 'meshoptimizer') options=(!strip) provides=('magnum-plugins-git') @@ -48,6 +48,7 @@ build() { -DWITH_HARFBUZZFONT=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ diff --git a/package/archlinux/PKGBUILD-mingw-w64 b/package/archlinux/PKGBUILD-mingw-w64 index f1da711e8..40b8e7eb4 100644 --- a/package/archlinux/PKGBUILD-mingw-w64 +++ b/package/archlinux/PKGBUILD-mingw-w64 @@ -33,6 +33,7 @@ build() { -DWITH_FREETYPEFONT=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ @@ -73,6 +74,7 @@ build() { -DWITH_FREETYPEFONT=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ diff --git a/package/archlinux/PKGBUILD-release b/package/archlinux/PKGBUILD-release index efb56c121..1e13cd627 100644 --- a/package/archlinux/PKGBUILD-release +++ b/package/archlinux/PKGBUILD-release @@ -6,7 +6,7 @@ pkgdesc="Plugins for the Magnum C++11/C++14 graphics engine (debug+release libs) arch=('i686' 'x86_64') url="https://magnum.graphics" license=('MIT') -depends=('magnum' 'devil' 'faad2' 'freetype2' 'harfbuzz' 'libjpeg' 'libpng' 'assimp') +depends=('magnum' 'devil' 'faad2' 'freetype2' 'harfbuzz' 'libjpeg' 'libpng' 'assimp' 'meshoptimizer') makedepends=('cmake' 'ninja' 'basis-universal-src') options=('!strip') provides=('magnum-plugins-git') @@ -36,6 +36,7 @@ build() { -DWITH_HARFBUZZFONT=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ @@ -73,6 +74,7 @@ build() { -DWITH_HARFBUZZFONT=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ diff --git a/package/archlinux/magnum-plugins-git/PKGBUILD b/package/archlinux/magnum-plugins-git/PKGBUILD index 7101cd96e..7a1b46929 100644 --- a/package/archlinux/magnum-plugins-git/PKGBUILD +++ b/package/archlinux/magnum-plugins-git/PKGBUILD @@ -6,7 +6,7 @@ pkgdesc="Plugins for the Magnum C++11/C++14 graphics engine (Git version)" arch=('i686' 'x86_64') url="https://magnum.graphics" license=('MIT') -depends=('magnum-git' 'devil' 'faad2' 'libpng' 'libjpeg' 'freetype2' 'assimp') +depends=('magnum-git' 'devil' 'faad2' 'libpng' 'libjpeg' 'freetype2' 'assimp' 'meshoptimizer') makedepends=('cmake' 'git') provides=('magnum-plugins') conflicts=('magnum-plugins') @@ -37,6 +37,7 @@ build() { -DWITH_FREETYPEFONT=ON \ -DWITH_JPEGIMPORTER=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ diff --git a/package/archlinux/magnum-plugins/PKGBUILD b/package/archlinux/magnum-plugins/PKGBUILD index cc851c46a..8d5d3d312 100644 --- a/package/archlinux/magnum-plugins/PKGBUILD +++ b/package/archlinux/magnum-plugins/PKGBUILD @@ -31,6 +31,7 @@ build() { -DWITH_FREETYPEFONT=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ diff --git a/package/ci/appveyor-desktop-mingw.bat b/package/ci/appveyor-desktop-mingw.bat index 1ae7c0ba9..6f1bc8a55 100644 --- a/package/ci/appveyor-desktop-mingw.bat +++ b/package/ci/appveyor-desktop-mingw.bat @@ -19,6 +19,20 @@ cmake .. ^ cmake --build . --target install || exit /b cd .. && cd .. || exit /b +rem build meshoptimizer +IF NOT EXIST %APPVEYOR_BUILD_FOLDER%\v0.14.zip appveyor DownloadFile https://github.com/zeux/meshoptimizer/archive/v0.14.zip || exit /b +7z x v0.14.zip || exit /b +ren meshoptimizer-0.14 meshoptimizer || exit /b +cd meshoptimizer || exit /b +mkdir build && cd build || exit /b +cmake .. ^ + -DCMAKE_CXX_FLAGS="--coverage" ^ + -DCMAKE_BUILD_TYPE=Debug ^ + -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/deps ^ + -G Ninja || exit /b +cmake --build . --target install || exit /b +cd .. && cd .. || exit /b + rem Build Corrade git clone --depth 1 git://github.com/mosra/corrade.git || exit /b cd corrade || exit /b @@ -78,6 +92,7 @@ cmake .. ^ -DWITH_HARFBUZZFONT=OFF ^ -DWITH_JPEGIMAGECONVERTER=ON ^ -DWITH_JPEGIMPORTER=ON ^ + -DWITH_MESHOPTIMIZERSCENECONVERTER=ON ^ -DWITH_MINIEXRIMAGECONVERTER=ON ^ -DWITH_OPENGEXIMPORTER=ON ^ -DWITH_PNGIMAGECONVERTER=OFF ^ diff --git a/package/ci/appveyor-desktop.bat b/package/ci/appveyor-desktop.bat index 2221a46d4..3ad5bd162 100644 --- a/package/ci/appveyor-desktop.bat +++ b/package/ci/appveyor-desktop.bat @@ -30,6 +30,19 @@ IF "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2015" set EXCEPT_IF_VCPKG_I IF NOT "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2015" set EXCEPT_IF_VCPKG_IS_BROKEN=ON IF NOT "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2015" vcpkg install libpng:x64-windows || exit /b +rem build meshoptimizer +IF NOT EXIST %APPVEYOR_BUILD_FOLDER%\v0.14.zip appveyor DownloadFile https://github.com/zeux/meshoptimizer/archive/v0.14.zip || exit /b +7z x v0.14.zip || exit /b +ren meshoptimizer-0.14 meshoptimizer || exit /b +cd meshoptimizer || exit /b +mkdir build && cd build || exit /b +cmake .. ^ + -DCMAKE_BUILD_TYPE=Debug ^ + -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/deps ^ + %COMPILER_EXTRA% -G Ninja || exit /b +cmake --build . --target install || exit /b +cd .. && cd .. || exit /b + rem Build Corrade git clone --depth 1 git://github.com/mosra/corrade.git || exit /b cd corrade || exit /b @@ -89,6 +102,7 @@ cmake .. ^ -DWITH_HARFBUZZFONT=OFF ^ -DWITH_JPEGIMAGECONVERTER=ON ^ -DWITH_JPEGIMPORTER=ON ^ + -DWITH_MESHOPTIMIZERSCENECONVERTER=ON ^ -DWITH_MINIEXRIMAGECONVERTER=ON ^ -DWITH_OPENGEXIMPORTER=ON ^ -DWITH_PNGIMAGECONVERTER=%EXCEPT_IF_VCPKG_IS_BROKEN% ^ diff --git a/package/ci/appveyor-rt.bat b/package/ci/appveyor-rt.bat index f937a246a..48f2580da 100644 --- a/package/ci/appveyor-rt.bat +++ b/package/ci/appveyor-rt.bat @@ -76,6 +76,7 @@ cmake .. ^ -DWITH_HARFBUZZFONT=OFF ^ -DWITH_JPEGIMAGECONVERTER=OFF ^ -DWITH_JPEGIMPORTER=OFF ^ + -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF ^ -DWITH_MINIEXRIMAGECONVERTER=ON ^ -DWITH_OPENGEXIMPORTER=ON ^ -DWITH_PNGIMAGECONVERTER=OFF ^ diff --git a/package/ci/appveyor.yml b/package/ci/appveyor.yml index 07fd64cb7..8b0b62c6e 100644 --- a/package/ci/appveyor.yml +++ b/package/ci/appveyor.yml @@ -73,3 +73,5 @@ cache: - libjpeg-turbo-1.5.0.tar.gz -> package/ci/appveyor-cache-reset.txt - DevIL-SDK-x64-1.7.8.zip -> package/ci/appveyor-cache-reset.txt - basis_universal-%BASIS_VERSION%.zip -> package/ci/appveyor-cache-reset.txt +# meshoptimizer +- v0.14.zip -> package/ci/appveyor-cache-reset.txt diff --git a/package/ci/travis-android-arm.sh b/package/ci/travis-android-arm.sh index 8ef4439f7..dde0464aa 100755 --- a/package/ci/travis-android-arm.sh +++ b/package/ci/travis-android-arm.sh @@ -89,6 +89,7 @@ cmake .. \ -DWITH_HARFBUZZFONT=OFF \ -DWITH_JPEGIMAGECONVERTER=OFF \ -DWITH_JPEGIMPORTER=OFF \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=OFF \ diff --git a/package/ci/travis-desktop.sh b/package/ci/travis-desktop.sh index 71fdd772b..e4dc2f810 100755 --- a/package/ci/travis-desktop.sh +++ b/package/ci/travis-desktop.sh @@ -65,6 +65,7 @@ cmake .. \ -DWITH_HARFBUZZFONT=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ diff --git a/package/ci/travis-emscripten.sh b/package/ci/travis-emscripten.sh index 2c43a6816..fb6ddad8d 100755 --- a/package/ci/travis-emscripten.sh +++ b/package/ci/travis-emscripten.sh @@ -98,6 +98,7 @@ cmake .. \ -DWITH_HARFBUZZFONT=OFF \ -DWITH_JPEGIMAGECONVERTER=OFF \ -DWITH_JPEGIMPORTER=OFF \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=OFF \ diff --git a/package/ci/travis-ios-simulator.sh b/package/ci/travis-ios-simulator.sh index 5e3667c30..f5a336bd6 100755 --- a/package/ci/travis-ios-simulator.sh +++ b/package/ci/travis-ios-simulator.sh @@ -81,6 +81,7 @@ cmake .. \ -DWITH_HARFBUZZFONT=OFF \ -DWITH_JPEGIMAGECONVERTER=OFF \ -DWITH_JPEGIMPORTER=OFF \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=OFF \ diff --git a/package/ci/travis.yml b/package/ci/travis.yml index e196e5665..cb7e79f98 100644 --- a/package/ci/travis.yml +++ b/package/ci/travis.yml @@ -165,6 +165,9 @@ notifications: cache: directories: - $HOME/cmake + # Not caching meshoptimizer build because it's small enough <3 and so it's + # faster to download and build than unpacking the cache. Would cache just + # the archive file, but Travis doesn't know how to do that, sigh. install: - if [ "$TRAVIS_OS_NAME" == "linux" ] && [ ! "$TARGET" == "desktop-sanitizers" ] && [ ! "$TARGET" == "android" ]; then export CXX=g++-4.8; fi @@ -193,6 +196,9 @@ install: # FAAD2, HarfBuzz, Assimp, DevIL - if [ "$TRAVIS_OS_NAME" == "osx" ] && [ "$TARGET" == "desktop" ]; then HOMEBREW_NO_AUTO_UPDATE=1 brew install faad2 harfbuzz assimp devil; fi +# meshoptimizer, few commits after 0.14 with a fix for old Apple Clang +- if [[ "$TARGET" == desktop* ]]; then wget -nc --no-check-certificate https://github.com/zeux/meshoptimizer/archive/97c52415c6d29f297a76482ddde22f739292446d.tar.gz && mkdir -p meshoptimizer && cd meshoptimizer && tar --strip-components=1 -xzf ../97c52415c6d29f297a76482ddde22f739292446d.tar.gz && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_INSTALL_PREFIX=$HOME/deps -G Ninja && ninja install && cd $TRAVIS_BUILD_DIR; fi + # Basis Universal - export BASIS_VERSION=8565af680d1bd2ad56ab227ca7d96c56dfbe93ed && wget -nc https://github.com/BinomialLLC/basis_universal/archive/$BASIS_VERSION.zip && unzip -q $BASIS_VERSION; mv basis_universal-$BASIS_VERSION $HOME/basis_universal diff --git a/package/debian/rules b/package/debian/rules index 22a4b64c9..fd2e9e8fb 100755 --- a/package/debian/rules +++ b/package/debian/rules @@ -27,6 +27,7 @@ override_dh_auto_configure: -DWITH_FREETYPEFONT=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ diff --git a/package/gentoo/dev-libs/magnum-plugins/magnum-plugins-9999.ebuild b/package/gentoo/dev-libs/magnum-plugins/magnum-plugins-9999.ebuild index a8bc06af0..b2b7b701a 100644 --- a/package/gentoo/dev-libs/magnum-plugins/magnum-plugins-9999.ebuild +++ b/package/gentoo/dev-libs/magnum-plugins/magnum-plugins-9999.ebuild @@ -42,6 +42,7 @@ src_configure() { -DWITH_HARFBUZZFONT=ON -DWITH_JPEGIMAGECONVERTER=ON -DWITH_JPEGIMPORTER=ON + -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF -DWITH_MINIEXRIMAGECONVERTER=ON -DWITH_OPENGEXIMPORTER=ON -DWITH_PNGIMAGECONVERTER=ON diff --git a/package/homebrew/magnum-plugins.rb b/package/homebrew/magnum-plugins.rb index f95bd0564..f43eb49e9 100644 --- a/package/homebrew/magnum-plugins.rb +++ b/package/homebrew/magnum-plugins.rb @@ -19,7 +19,7 @@ class MagnumPlugins < Formula def install system "mkdir build" cd "build" do - system "cmake", "-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_INSTALL_PREFIX=#{prefix}", "-DWITH_ASSIMPIMPORTER=ON", "-DWITH_BASISIMAGECONVERTER=OFF", "-DWITH_BASISIMPORTER=OFF", "-DWITH_DDSIMPORTER=ON", "-DWITH_DEVILIMAGEIMPORTER=ON", "-DWITH_DRFLACAUDIOIMPORTER=ON", "-DWITH_DRMP3AUDIOIMPORTER=ON", "-DWITH_DRWAVAUDIOIMPORTER=ON", "-DWITH_FAAD2AUDIOIMPORTER=ON", "-DWITH_FREETYPEFONT=ON", "-DWITH_HARFBUZZFONT=ON", "-DWITH_JPEGIMAGECONVERTER=ON", "-DWITH_JPEGIMPORTER=ON", "-DWITH_MINIEXRIMAGECONVERTER=ON", "-DWITH_OPENGEXIMPORTER=ON", "-DWITH_PNGIMAGECONVERTER=ON", "-DWITH_PNGIMPORTER=ON", "-DWITH_PRIMITIVEIMPORTER=ON", "-DWITH_STANFORDIMPORTER=ON", "-DWITH_STANFORDSCENECONVERTER=ON", "-DWITH_STBIMAGECONVERTER=ON", "-DWITH_STBIMAGEIMPORTER=ON", "-DWITH_STBTRUETYPEFONT=ON", "-DWITH_STBVORBISAUDIOIMPORTER=ON", "-DWITH_STLIMPORTER=ON", "-DWITH_TINYGLTFIMPORTER=ON", ".." + system "cmake", "-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_INSTALL_PREFIX=#{prefix}", "-DWITH_ASSIMPIMPORTER=ON", "-DWITH_BASISIMAGECONVERTER=OFF", "-DWITH_BASISIMPORTER=OFF", "-DWITH_DDSIMPORTER=ON", "-DWITH_DEVILIMAGEIMPORTER=ON", "-DWITH_DRFLACAUDIOIMPORTER=ON", "-DWITH_DRMP3AUDIOIMPORTER=ON", "-DWITH_DRWAVAUDIOIMPORTER=ON", "-DWITH_FAAD2AUDIOIMPORTER=ON", "-DWITH_FREETYPEFONT=ON", "-DWITH_HARFBUZZFONT=ON", "-DWITH_JPEGIMAGECONVERTER=ON", "-DWITH_JPEGIMPORTER=ON", "-DWITH_MESHOPTIMIZERSCENECONVERTER=OFF", "-DWITH_MINIEXRIMAGECONVERTER=ON", "-DWITH_OPENGEXIMPORTER=ON", "-DWITH_PNGIMAGECONVERTER=ON", "-DWITH_PNGIMPORTER=ON", "-DWITH_PRIMITIVEIMPORTER=ON", "-DWITH_STANFORDIMPORTER=ON", "-DWITH_STANFORDSCENECONVERTER=ON", "-DWITH_STBIMAGECONVERTER=ON", "-DWITH_STBIMAGEIMPORTER=ON", "-DWITH_STBTRUETYPEFONT=ON", "-DWITH_STBVORBISAUDIOIMPORTER=ON", "-DWITH_STLIMPORTER=ON", "-DWITH_TINYGLTFIMPORTER=ON", ".." system "cmake", "--build", "." system "cmake", "--build", ".", "--target", "install" end diff --git a/package/msys/PKGBUILD b/package/msys/PKGBUILD index e387564eb..70ac9a7b2 100644 --- a/package/msys/PKGBUILD +++ b/package/msys/PKGBUILD @@ -46,6 +46,7 @@ build() { -DWITH_HARFBUZZFONT=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ diff --git a/package/msys/magnum-plugins/PKGBUILD b/package/msys/magnum-plugins/PKGBUILD index dfb6659da..42b9f7de6 100644 --- a/package/msys/magnum-plugins/PKGBUILD +++ b/package/msys/magnum-plugins/PKGBUILD @@ -54,6 +54,7 @@ build() { -DWITH_HARFBUZZFONT=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ -DWITH_JPEGIMPORTER=ON \ + -DWITH_MESHOPTIMIZERSCENECONVERTER=OFF \ -DWITH_MINIEXRIMAGECONVERTER=ON \ -DWITH_OPENGEXIMPORTER=ON \ -DWITH_PNGIMAGECONVERTER=ON \ diff --git a/src/MagnumPlugins/CMakeLists.txt b/src/MagnumPlugins/CMakeLists.txt index 5f21f4a13..1579d6409 100644 --- a/src/MagnumPlugins/CMakeLists.txt +++ b/src/MagnumPlugins/CMakeLists.txt @@ -86,6 +86,10 @@ if(WITH_JPEGIMPORTER) add_subdirectory(JpegImporter) endif() +if(WITH_MESHOPTIMIZERSCENECONVERTER) + add_subdirectory(MeshOptimizerSceneConverter) +endif() + if(WITH_MINIEXRIMAGECONVERTER) add_subdirectory(MiniExrImageConverter) endif() diff --git a/src/MagnumPlugins/MeshOptimizerSceneConverter/CMakeLists.txt b/src/MagnumPlugins/MeshOptimizerSceneConverter/CMakeLists.txt new file mode 100644 index 000000000..422287dcf --- /dev/null +++ b/src/MagnumPlugins/MeshOptimizerSceneConverter/CMakeLists.txt @@ -0,0 +1,80 @@ +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 +# Vladimír Vondruš +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# + +find_package(Magnum REQUIRED MeshTools Trade) + +if(NOT TARGET meshoptimizer) + find_package(meshoptimizer REQUIRED CONFIG) +else() + add_library(meshoptimizer::meshoptimizer ALIAS meshoptimizer) +endif() + +if(BUILD_PLUGINS_STATIC) + set(MAGNUM_MESHOPTIMIZERSCENECONVERTER_BUILD_STATIC 1) +endif() + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/configure.h) + +# MeshOptimizerSceneConverter plugin +add_plugin(MeshOptimizerSceneConverter + "${MAGNUM_PLUGINS_SCENECONVERTER_DEBUG_BINARY_INSTALL_DIR};${MAGNUM_PLUGINS_SCENECONVERTER_DEBUG_LIBRARY_INSTALL_DIR}" + "${MAGNUM_PLUGINS_SCENECONVERTER_RELEASE_BINARY_INSTALL_DIR};${MAGNUM_PLUGINS_SCENECONVERTER_RELEASE_LIBRARY_INSTALL_DIR}" + MeshOptimizerSceneConverter.conf + MeshOptimizerSceneConverter.cpp + MeshOptimizerSceneConverter.h) +if(BUILD_PLUGINS_STATIC AND BUILD_STATIC_PIC) + set_target_properties(MeshOptimizerSceneConverter PROPERTIES POSITION_INDEPENDENT_CODE ON) +endif() +target_include_directories(MeshOptimizerSceneConverter PUBLIC + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_BINARY_DIR}/src) +target_link_libraries(MeshOptimizerSceneConverter PUBLIC + Magnum::MeshTools + Magnum::Trade + meshoptimizer::meshoptimizer) +# Modify output location only if all are set, otherwise it makes no sense +if(CMAKE_RUNTIME_OUTPUT_DIRECTORY AND CMAKE_LIBRARY_OUTPUT_DIRECTORY AND CMAKE_ARCHIVE_OUTPUT_DIRECTORY) + set_target_properties(MeshOptimizerSceneConverter PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/magnum$<$:-d>/sceneconverters + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/magnum$<$:-d>/sceneconverters + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/magnum$<$:-d>/sceneconverters) +endif() + +install(FILES MeshOptimizerSceneConverter.h ${CMAKE_CURRENT_BINARY_DIR}/configure.h + DESTINATION ${MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR}/MeshOptimizerSceneConverter) + +# Automatic static plugin import +if(BUILD_PLUGINS_STATIC) + install(FILES importStaticPlugin.cpp DESTINATION ${MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR}/MeshOptimizerSceneConverter) + target_sources(MeshOptimizerSceneConverter INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/importStaticPlugin.cpp) +endif() + +if(BUILD_TESTS) + add_subdirectory(Test) +endif() + +# MagnumPlugins MeshOptimizerSceneConverter target alias for superprojects +add_library(MagnumPlugins::MeshOptimizerSceneConverter ALIAS MeshOptimizerSceneConverter) diff --git a/src/MagnumPlugins/MeshOptimizerSceneConverter/MeshOptimizerSceneConverter.conf b/src/MagnumPlugins/MeshOptimizerSceneConverter/MeshOptimizerSceneConverter.conf new file mode 100644 index 000000000..f3a3d6067 --- /dev/null +++ b/src/MagnumPlugins/MeshOptimizerSceneConverter/MeshOptimizerSceneConverter.conf @@ -0,0 +1,19 @@ +# [config] +[configuration] +# Vertex cache optimization, operates on the index buffer only +optimizeVertexCache=true + +# Overdraw optimization, operates on the index buffer and additionally requires +# the mesh to provide (read-only) per-vertex positions +optimizeOverdraw=true +optimizeOverdrawThreshold=1.05 + +# Vertex fetch optimization, operates on both index and vertex buffer +optimizeVertexFetch=true + +# Used by mesh efficiency analyzers when verbose output is enabled. Defaults +# the same as in the meshoptimizer demo app. +analyzeCacheSize=16 +analyzeWarpSize=0 +analyzePrimitiveGroupSize=0 +# [config] diff --git a/src/MagnumPlugins/MeshOptimizerSceneConverter/MeshOptimizerSceneConverter.cpp b/src/MagnumPlugins/MeshOptimizerSceneConverter/MeshOptimizerSceneConverter.cpp new file mode 100644 index 000000000..101202f65 --- /dev/null +++ b/src/MagnumPlugins/MeshOptimizerSceneConverter/MeshOptimizerSceneConverter.cpp @@ -0,0 +1,289 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "MeshOptimizerSceneConverter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Magnum { namespace Trade { + +MeshOptimizerSceneConverter::MeshOptimizerSceneConverter(PluginManager::AbstractManager& manager, const std::string& plugin): AbstractSceneConverter{manager, plugin} {} + +MeshOptimizerSceneConverter::~MeshOptimizerSceneConverter() = default; + +SceneConverterFeatures MeshOptimizerSceneConverter::doFeatures() const { + return SceneConverterFeature::ConvertMeshInPlace|SceneConverterFeature::ConvertMesh; +} + +namespace { + +template void analyze(const MeshData& mesh, const Utility::ConfigurationGroup& configuration, const UnsignedInt vertexSize, const Containers::StridedArrayView1D positions, meshopt_VertexCacheStatistics& vertexCacheStats, meshopt_VertexFetchStatistics& vertexFetchStats, meshopt_OverdrawStatistics& overdrawStats) { + const auto indices = mesh.indices(); + vertexCacheStats = meshopt_analyzeVertexCache(indices.data(), mesh.indexCount(), mesh.vertexCount(), configuration.value("analyzeCacheSize"), configuration.value("analyzeWarpSize"), configuration.value("analyzePrimitiveGroupSize")); + if(vertexSize) vertexFetchStats = meshopt_analyzeVertexFetch(indices.data(), mesh.indexCount(), mesh.vertexCount(), vertexSize); + if(positions) overdrawStats = meshopt_analyzeOverdraw(indices.data(), mesh.indexCount(), static_cast(positions.data()), mesh.vertexCount(), positions.stride()); +} + +void analyze(const MeshData& mesh, const Utility::ConfigurationGroup& configuration, const Containers::StridedArrayView1D positions, Containers::Optional& vertexSize, meshopt_VertexCacheStatistics& vertexCacheStats, meshopt_VertexFetchStatistics& vertexFetchStats, meshopt_OverdrawStatistics& overdrawStats) { + /* Calculate vertex size out of all attributes. If any attribute is + implementation-specific, do nothing (warning will be printed by the + caller) */ + if(!vertexSize) { + vertexSize = 0; + for(UnsignedInt i = 0; i != mesh.attributeCount(); ++i) { + VertexFormat format = mesh.attributeFormat(i); + const UnsignedInt arraySize = mesh.attributeArraySize(i); + if(isVertexFormatImplementationSpecific(format)) { + vertexSize = 0; + break; + } + *vertexSize += vertexFormatSize(format)*(arraySize ? arraySize : 1); + } + } + + if(mesh.indexType() == MeshIndexType::UnsignedInt) + analyze(mesh, configuration, *vertexSize, positions, vertexCacheStats, vertexFetchStats, overdrawStats); + else if(mesh.indexType() == MeshIndexType::UnsignedShort) + analyze(mesh, configuration, *vertexSize, positions, vertexCacheStats, vertexFetchStats, overdrawStats); + else if(mesh.indexType() == MeshIndexType::UnsignedByte) + analyze(mesh, configuration, *vertexSize, positions, vertexCacheStats, vertexFetchStats, overdrawStats); + else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +} + +void analyzePost(const char* prefix, const MeshData& mesh, const Utility::ConfigurationGroup& configuration, const Containers::StridedArrayView1D positions, Containers::Optional& vertexSize, meshopt_VertexCacheStatistics& vertexCacheStatsBefore, meshopt_VertexFetchStatistics& vertexFetchStatsBefore, meshopt_OverdrawStatistics& overdrawStatsBefore) { + /* If vertex size is zero, it means there was an implementation-specific + vertex format somewhere. Print a warning about that. */ + CORRADE_INTERNAL_ASSERT(vertexSize); + if(!*vertexSize) for(UnsignedInt i = 0; i != mesh.attributeCount(); ++i) { + VertexFormat format = mesh.attributeFormat(i); + if(isVertexFormatImplementationSpecific(format)) { + Warning{} << prefix << "can't analyze vertex fetch for" << format; + break; + } + } + + meshopt_VertexCacheStatistics vertexCacheStats; + meshopt_VertexFetchStatistics vertexFetchStats; + meshopt_OverdrawStatistics overdrawStats; + analyze(mesh, configuration, positions, vertexSize, vertexCacheStats, vertexFetchStats, overdrawStats); + + Debug{} << prefix << "processing stats:"; + Debug{} << " vertex cache:\n " + << vertexCacheStatsBefore.vertices_transformed << "->" + << vertexCacheStats.vertices_transformed + << "transformed vertices\n " + << vertexCacheStatsBefore.warps_executed << "->" + << vertexCacheStats.warps_executed << "executed warps\n ACMR" + << vertexCacheStatsBefore.acmr << "->" << vertexCacheStats.acmr + << Debug::newline << " ATVR" << vertexCacheStatsBefore.atvr + << "->" << vertexCacheStats.atvr; + if(*vertexSize) Debug{} << " vertex fetch:\n " + << vertexFetchStatsBefore.bytes_fetched << "->" + << vertexFetchStats.bytes_fetched << "bytes fetched\n overfetch" + << vertexFetchStatsBefore.overfetch << "->" + << vertexFetchStats.overfetch; + if(positions) Debug{} << " overdraw:\n " + << overdrawStatsBefore.pixels_shaded << "->" + << overdrawStats.pixels_shaded << "shaded pixels\n " + << overdrawStatsBefore.pixels_covered << "->" + << overdrawStats.pixels_covered << "covered pixels\n overdraw" + << overdrawStatsBefore.overdraw << "->" << overdrawStats.overdraw; +} + +bool convertInPlaceInternal(const char* prefix, MeshData& mesh, const SceneConverterFlags flags, const Utility::ConfigurationGroup& configuration, Containers::Array& positionStorage, Containers::StridedArrayView1D& positions, Containers::Optional& vertexSize, meshopt_VertexCacheStatistics& vertexCacheStatsBefore, meshopt_VertexFetchStatistics& vertexFetchStatsBefore, meshopt_OverdrawStatistics& overdrawStatsBefore) { + /* Only doConvert() can handle triangle strips etc, in-place only triangles */ + if(mesh.primitive() != MeshPrimitive::Triangles) { + Error{} << prefix << "expected a triangle mesh, got" << mesh.primitive(); + return false; + } + + /* Can't really do anything with non-indexed meshes, sorry */ + if(!mesh.isIndexed()) { + Error{} << prefix << "expected an indexed mesh"; + return false; + } + + /* If we need it, get the position attribute, unpack if packed. It's used + by the verbose stats also but in that case the processing shouldn't fail + if there are no positions -- so check the hasAttribute() earlier. */ + if((flags & SceneConverterFlag::Verbose && mesh.hasAttribute(MeshAttribute::Position)) || + configuration.value("optimizeOverdraw")) + { + if(!mesh.hasAttribute(MeshAttribute::Position)) { + Error{} << prefix << "optimizeOverdraw requires the mesh to have positions"; + return false; + } + + if(mesh.attributeFormat(MeshAttribute::Position) == VertexFormat::Vector3) + positions = mesh.attribute(MeshAttribute::Position); + else { + positionStorage = mesh.positions3DAsArray(); + positions = positionStorage; + } + } + + /* Save "before" stats if verbose output is requested. No messages as those + will be printed only at the end if the processing passes. */ + if(flags & SceneConverterFlag::Verbose) { + analyze(mesh, configuration, positions, vertexSize, vertexCacheStatsBefore, vertexFetchStatsBefore, overdrawStatsBefore); + } + + /* Vertex cache optimization. Goes first. */ + if(configuration.value("optimizeVertexCache")) { + if(mesh.indexType() == MeshIndexType::UnsignedInt) { + Containers::ArrayView indices = mesh.mutableIndices(); + meshopt_optimizeVertexCache(indices.data(), indices.data(), mesh.indexCount(), mesh.vertexCount()); + } else if(mesh.indexType() == MeshIndexType::UnsignedShort) { + Containers::ArrayView indices = mesh.mutableIndices(); + meshopt_optimizeVertexCache(indices.data(), indices.data(), mesh.indexCount(), mesh.vertexCount()); + } else if(mesh.indexType() == MeshIndexType::UnsignedByte) { + Containers::ArrayView indices = mesh.mutableIndices(); + meshopt_optimizeVertexCache(indices.data(), indices.data(), mesh.indexCount(), mesh.vertexCount()); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + + /* Overdraw optimization. Goes after vertex cache optimization. */ + if(configuration.value("optimizeOverdraw")) { + const Float optimizeOverdrawThreshold = configuration.value("optimizeOverdrawThreshold"); + + if(mesh.indexType() == MeshIndexType::UnsignedInt) { + Containers::ArrayView indices = mesh.mutableIndices(); + meshopt_optimizeOverdraw(indices.data(), indices.data(), mesh.indexCount(), static_cast(positions.data()), mesh.vertexCount(), positions.stride(), optimizeOverdrawThreshold); + } else if(mesh.indexType() == MeshIndexType::UnsignedShort) { + Containers::ArrayView indices = mesh.mutableIndices(); + meshopt_optimizeOverdraw(indices.data(), indices.data(), mesh.indexCount(), static_cast(positions.data()), mesh.vertexCount(), positions.stride(), optimizeOverdrawThreshold); + } else if(mesh.indexType() == MeshIndexType::UnsignedByte) { + Containers::ArrayView indices = mesh.mutableIndices(); + meshopt_optimizeOverdraw(indices.data(), indices.data(), mesh.indexCount(), static_cast(positions.data()), mesh.vertexCount(), positions.stride(), optimizeOverdrawThreshold); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + + /* Vertex fetch optimization. Goes after overdraw optimization. Reorders + the vertex buffer for better memory locality, so if we have no + attributes it's of no use (also meshoptimizer asserts in that case). + Skipping silently instead of failing hard, as an attribute-less mesh + always *is* optimized for vertex fetch, so there's nothing wrong. */ + if(configuration.value("optimizeVertexFetch") && mesh.attributeCount()) { + /* This assumes the mesh is interleaved. doConvert() already ensures + that, doConvertInPlace() has a runtime check */ + Containers::StridedArrayView2D interleavedData = MeshTools::interleavedMutableData(mesh); + + if(mesh.indexType() == MeshIndexType::UnsignedInt) { + Containers::ArrayView indices = mesh.mutableIndices(); + meshopt_optimizeVertexFetch(interleavedData.data(), indices.data(), mesh.indexCount(), interleavedData.data(), mesh.vertexCount(), interleavedData.stride()[0]); + } else if(mesh.indexType() == MeshIndexType::UnsignedShort) { + Containers::ArrayView indices = mesh.mutableIndices(); + meshopt_optimizeVertexFetch(interleavedData.data(), indices.data(), mesh.indexCount(), interleavedData.data(), mesh.vertexCount(), interleavedData.stride()[0]); + } else if(mesh.indexType() == MeshIndexType::UnsignedByte) { + Containers::ArrayView indices = mesh.mutableIndices(); + meshopt_optimizeVertexFetch(interleavedData.data(), indices.data(), mesh.indexCount(), interleavedData.data(), mesh.vertexCount(), interleavedData.stride()[0]); + } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ + } + + return true; +} + +} + +bool MeshOptimizerSceneConverter::doConvertInPlace(MeshData& mesh) { + if((configuration().value("optimizeVertexCache") || + configuration().value("optimizeOverdraw") || + configuration().value("optimizeVertexFetch")) && + !(mesh.indexDataFlags() & DataFlag::Mutable)) + { + Error{} << "Trade::MeshOptimizerSceneConverter::convertInPlace(): optimizeVertexCache, optimizeOverdraw and optimizeVertexFetch require index data to be mutable"; + return false; + } + + if(configuration().value("optimizeVertexFetch")) { + if(!(mesh.vertexDataFlags() & DataFlag::Mutable)) { + Error{} << "Trade::MeshOptimizerSceneConverter::convertInPlace(): optimizeVertexFetch requires vertex data to be mutable"; + return false; + } + + if(!MeshTools::isInterleaved(mesh)) { + Error{} << "Trade::MeshOptimizerSceneConverter::convertInPlace(): optimizeVertexFetch requires the mesh to be interleaved"; + return false; + } + } + + meshopt_VertexCacheStatistics vertexCacheStatsBefore; + meshopt_VertexFetchStatistics vertexFetchStatsBefore; + meshopt_OverdrawStatistics overdrawStatsBefore; + Containers::Array positionStorage; + Containers::StridedArrayView1D positions; + Containers::Optional vertexSize; + if(!convertInPlaceInternal("Trade::MeshOptimizerSceneConverter::convertInPlace():", mesh, flags(), configuration(), positionStorage, positions, vertexSize, vertexCacheStatsBefore, vertexFetchStatsBefore, overdrawStatsBefore)) + return false; + + if(flags() & SceneConverterFlag::Verbose) + analyzePost("Trade::MeshOptimizerSceneConverter::convertInPlace():", mesh, configuration(), positions, vertexSize, vertexCacheStatsBefore, vertexFetchStatsBefore, overdrawStatsBefore); + + return true; +} + +Containers::Optional MeshOptimizerSceneConverter::doConvert(const MeshData& mesh) { + /* Make the mesh interleaved and owned first */ + /** @todo concat() unpacks the indices :/ */ + /** @todo interleave() makes the mesh owned for some :/ */ + MeshData out = MeshTools::concatenate(MeshTools::interleave(mesh)); + CORRADE_INTERNAL_ASSERT(MeshTools::isInterleaved(out)); + + /* Convert to an indexed triangle mesh if we have a strip or a fan */ + if(out.primitive() == MeshPrimitive::TriangleStrip || out.primitive() == MeshPrimitive::TriangleFan) { + if(out.isIndexed()) out = MeshTools::duplicate(out); + out = MeshTools::generateIndices(std::move(out)); + } + + meshopt_VertexCacheStatistics vertexCacheStatsBefore; + meshopt_VertexFetchStatistics vertexFetchStatsBefore; + meshopt_OverdrawStatistics overdrawStatsBefore; + Containers::Array positionStorage; + Containers::StridedArrayView1D positions; + Containers::Optional vertexSize; + if(!convertInPlaceInternal("Trade::MeshOptimizerSceneConverter::convert():", out, flags(), configuration(), positionStorage, positions, vertexSize, vertexCacheStatsBefore, vertexFetchStatsBefore, overdrawStatsBefore)) + return Containers::NullOpt; + + /* Print before & after stats if verbose output is requested */ + if(flags() & SceneConverterFlag::Verbose) + analyzePost("Trade::MeshOptimizerSceneConverter::convert():", out, configuration(), positions, vertexSize, vertexCacheStatsBefore, vertexFetchStatsBefore, overdrawStatsBefore); + + return out; +} + +}} + +CORRADE_PLUGIN_REGISTER(MeshOptimizerSceneConverter, Magnum::Trade::MeshOptimizerSceneConverter, + "cz.mosra.magnum.Trade.AbstractSceneConverter/0.1") diff --git a/src/MagnumPlugins/MeshOptimizerSceneConverter/MeshOptimizerSceneConverter.h b/src/MagnumPlugins/MeshOptimizerSceneConverter/MeshOptimizerSceneConverter.h new file mode 100644 index 000000000..553875b88 --- /dev/null +++ b/src/MagnumPlugins/MeshOptimizerSceneConverter/MeshOptimizerSceneConverter.h @@ -0,0 +1,163 @@ +#ifndef Magnum_Trade_MeshOptimizerSceneConverter_h +#define Magnum_Trade_MeshOptimizerSceneConverter_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class @ref Magnum::Trade::MeshOptimizerSceneConverter + * @m_since_latest_{plugins} + */ + +#include + +#include "MagnumPlugins/MeshOptimizerSceneConverter/configure.h" + +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_MESHOPTIMIZERSCENECONVERTER_BUILD_STATIC + #ifdef MeshOptimizerSceneConverter_EXPORTS + #define MAGNUM_MESHOPTIMIZERSCENECONVERTER_EXPORT CORRADE_VISIBILITY_EXPORT + #else + #define MAGNUM_MESHOPTIMIZERSCENECONVERTER_EXPORT CORRADE_VISIBILITY_IMPORT + #endif +#else + #define MAGNUM_MESHOPTIMIZERSCENECONVERTER_EXPORT CORRADE_VISIBILITY_STATIC +#endif +#define MAGNUM_MESHOPTIMIZERSCENECONVERTER_LOCAL CORRADE_VISIBILITY_LOCAL +#else +#define MAGNUM_MESHOPTIMIZERSCENECONVERTER_EXPORT +#define MAGNUM_MESHOPTIMIZERSCENECONVERTER_LOCAL +#endif + +namespace Magnum { namespace Trade { + +/** +@brief MeshOptimizer converter plugin +@m_since_latest_{plugins} + +Integrates various algorithms from [meshoptimizer](https://github.com/zeux/meshoptimizer). + +@m_class{m-block m-success} + +@thirdparty This plugin makes use of the + [meshoptimizer](https://github.com/zeux/meshoptimizer) library by Arseny + Kapoulkine, released under @m_class{m-label m-success} **MIT** + ([license text](https://github.com/zeux/meshoptimizer/blob/master/LICENSE.md), + [choosealicense.com](https://choosealicense.com/licenses/mit/)). + +@section Trade-MeshOptimizerSceneConverter-usage Usage + +This plugin depends on the @ref Trade library and is built if +`WITH_MESHOPTIMIZERSCENECONVERTER` is enabled when building Magnum Plugins. To +use as a dynamic plugin, load @cpp "MeshOptimizerSceneConverter" @ce via +@ref Corrade::PluginManager::Manager. + +Additionally, if you're using Magnum as a CMake subproject, bundle the +[magnum-plugins repository](https://github.com/mosra/magnum-plugins) and +[meshoptimizer](https://github.com/zeux/meshoptimizer) repositories and do the +following. If you want to use system-installed meshoptimizer, omit the first +part and point `CMAKE_PREFIX_PATH` to its installation dir if necessary. + +@code{.cmake} +set(CMAKE_POSITION_INDEPENDENT_CODE ON) # needed if building dynamic plugins +add_subdirectory(meshoptimizer EXCLUDE_FROM_ALL) + +set(WITH_MESHOPTIMIZERSCENECONVERTER ON CACHE BOOL "" FORCE) +add_subdirectory(magnum-plugins EXCLUDE_FROM_ALL) + +# So the dynamically loaded plugin gets built implicitly +add_dependencies(your-app MagnumPlugins::MeshOptimizerSceneConverter) +@endcode + +To use as a static plugin or as a dependency of another plugin with CMake, put +[FindMagnumPlugins.cmake](https://github.com/mosra/magnum-plugins/blob/master/modules/FindMagnumPlugins.cmake) +into your `modules/` directory, request the `MeshOptimizerSceneConverter` component of +the `MagnumPlugins` package and link to the +`MagnumPlugins::MeshOptimizerSceneConverter` target: + +@code{.cmake} +find_package(MagnumPlugins REQUIRED MeshOptimizerSceneConverter) + +# ... +target_link_libraries(your-app PRIVATE MagnumPlugins::MeshOptimizerSceneConverter) +@endcode + +See @ref building-plugins, @ref cmake-plugins and @ref plugins for more +information. + +@section Trade-MeshOptimizerSceneConverter-behavior Behavior and limitations + +The plugin by default performs the following optimizations, which can be +@ref Trade-MeshOptimizerSceneConverter-configuration "configured further" using +plugin-specific options: + +- [Vertex cache optimization](https://github.com/zeux/meshoptimizer#vertex-cache-optimization), + performed when @cb{.ini} optimizeVertexCache @ce is enabled +- [Overdraw optimization](https://github.com/zeux/meshoptimizer#overdraw-optimization), + performed when @cb{.ini} optimizeOverdraw @ce is enabled +- [Vertex fetch optimization](https://github.com/zeux/meshoptimizer#vertex-fetch-optimization), + performed when @cb{.ini} optimizeVertexFetch @ce is enabled + +The optimizations can be done either in-place using @ref convertInPlace(MeshData&), +in which case the input is required to be an indexed triangle mesh with mutable +index data and, in case of @cb{.ini} optimizeVertexFetch @ce, also mutable +vertex data. Alternatively, the operation can be performed using +@ref convert(const MeshData&), which accepts also triangle strips and fans, +returning always an indexed triangle mesh without requiring the input to be +mutable. + +The output has the same index type as input and all attributes are preserved, +including custom attributes and attributes with implementation-specific vertex +formats, except for @cb{.ini} optimizeOverdraw @ce, which needs a position +attribute in a known type. + +When @ref SceneConverterFlag::Verbose is enabled, the plugin prints the output +from meshoptimizer's [efficiency analyzers](https://github.com/zeux/meshoptimizer#efficiency-analyzers) +before and after the operation. + +@section Trade-MeshOptimizerSceneConverter-configuration Plugin-specific config + +It's possible to tune various output options through @ref configuration(). See +below for all options and their default values: + +@snippet MagnumPlugins/MeshOptimizerSceneConverter/MeshOptimizerSceneConverter.conf config + +*/ +class MAGNUM_MESHOPTIMIZERSCENECONVERTER_EXPORT MeshOptimizerSceneConverter: public AbstractSceneConverter { + public: + /** @brief Plugin manager constructor */ + explicit MeshOptimizerSceneConverter(PluginManager::AbstractManager& manager, const std::string& plugin); + + ~MeshOptimizerSceneConverter(); + + private: + MAGNUM_MESHOPTIMIZERSCENECONVERTER_LOCAL SceneConverterFeatures doFeatures() const override; + + MAGNUM_MESHOPTIMIZERSCENECONVERTER_LOCAL bool doConvertInPlace(MeshData& mesh) override; + MAGNUM_MESHOPTIMIZERSCENECONVERTER_LOCAL Containers::Optional doConvert(const MeshData& mesh) override; +}; + +}} + +#endif diff --git a/src/MagnumPlugins/MeshOptimizerSceneConverter/Test/CMakeLists.txt b/src/MagnumPlugins/MeshOptimizerSceneConverter/Test/CMakeLists.txt new file mode 100644 index 000000000..7c7bc7465 --- /dev/null +++ b/src/MagnumPlugins/MeshOptimizerSceneConverter/Test/CMakeLists.txt @@ -0,0 +1,69 @@ +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 +# Vladimír Vondruš +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# + +find_package(Magnum REQUIRED + MeshTools + Primitives) + +if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) + set(MAGNUMIMPORTER_TEST_DIR ".") +else() + set(MAGNUMIMPORTER_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../MagnumImporter/Test) +endif() + +# CMake before 3.8 has broken $ expressions for iOS (see +# https://gitlab.kitware.com/cmake/cmake/merge_requests/404) and since Corrade +# doesn't support dynamic plugins on iOS, this sorta works around that. Should +# be revisited when updating Travis to newer Xcode (xcode7.3 has CMake 3.6). +if(NOT BUILD_PLUGINS_STATIC) + set(MESHOPTIMIZERSCENECONVERTER_PLUGIN_FILENAME $) +endif() + +# First replace ${} variables, then $<> generator expressions +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) +file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h + INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) + +corrade_add_test(MeshOptimizerSceneConverterTest MeshOptimizerSceneConverterTest.cpp + LIBRARIES + Magnum::MeshTools + Magnum::Primitives + Magnum::Trade) +target_include_directories(MeshOptimizerSceneConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) +if(BUILD_PLUGINS_STATIC) + target_link_libraries(MeshOptimizerSceneConverterTest PRIVATE MeshOptimizerSceneConverter) +else() + # So the plugins get properly built when building the test + add_dependencies(MeshOptimizerSceneConverterTest MeshOptimizerSceneConverter) +endif() +set_target_properties(MeshOptimizerSceneConverterTest PROPERTIES FOLDER "MagnumPlugins/MeshOptimizerSceneConverter/Test") +if(CORRADE_BUILD_STATIC AND NOT BUILD_PLUGINS_STATIC) + # CMake < 3.4 does this implicitly, but 3.4+ not anymore (see CMP0065). + # That's generally okay, *except if* the build is static, the executable + # uses a plugin manager and needs to share globals with the plugins (such + # as output redirection and so on). + set_target_properties(MeshOptimizerSceneConverterTest PROPERTIES ENABLE_EXPORTS ON) +endif() diff --git a/src/MagnumPlugins/MeshOptimizerSceneConverter/Test/MeshOptimizerSceneConverterTest.cpp b/src/MagnumPlugins/MeshOptimizerSceneConverter/Test/MeshOptimizerSceneConverterTest.cpp new file mode 100644 index 000000000..e09950e29 --- /dev/null +++ b/src/MagnumPlugins/MeshOptimizerSceneConverter/Test/MeshOptimizerSceneConverterTest.cpp @@ -0,0 +1,699 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "configure.h" + +namespace Magnum { namespace Trade { namespace Test { namespace { + +struct MeshOptimizerSceneConverterTest: TestSuite::Tester { + explicit MeshOptimizerSceneConverterTest(); + + void notTriangles(); + void notIndexed(); + void immutableIndexData(); + + void inPlaceOptimizeVertexFetchImmutableVertexData(); + void inPlaceOptimizeVertexFetchNotInterleaved(); + void inPlaceOptimizeOverdrawNoPositions(); + + void inPlaceOptimizeNone(); + + template void inPlaceOptimizeVertexCache(); + template void inPlaceOptimizeOverdraw(); + template void inPlaceOptimizeVertexFetch(); + void inPlaceOptimizeVertexFetchNoAttributes(); + + template void inPlaceOptimizeEmpty(); + + template void verbose(); + void verboseCustomAttribute(); + void verboseImplementationSpecificAttribute(); + + /* Those test the copy-making function */ + void copy(); + void copyTriangleStrip2DPositions(); + void copyTriangleFanIndexed(); + + /* Explicitly forbid system-wide plugin dependencies */ + PluginManager::Manager _manager{"nonexistent"}; +}; + +MeshOptimizerSceneConverterTest::MeshOptimizerSceneConverterTest() { + addTests({ + &MeshOptimizerSceneConverterTest::notTriangles, + &MeshOptimizerSceneConverterTest::notIndexed, + &MeshOptimizerSceneConverterTest::immutableIndexData, + &MeshOptimizerSceneConverterTest::inPlaceOptimizeVertexFetchImmutableVertexData, + &MeshOptimizerSceneConverterTest::inPlaceOptimizeVertexFetchNotInterleaved, + &MeshOptimizerSceneConverterTest::inPlaceOptimizeOverdrawNoPositions, + + &MeshOptimizerSceneConverterTest::inPlaceOptimizeNone, + + &MeshOptimizerSceneConverterTest::inPlaceOptimizeVertexCache, + &MeshOptimizerSceneConverterTest::inPlaceOptimizeVertexCache, + &MeshOptimizerSceneConverterTest::inPlaceOptimizeVertexCache, + + &MeshOptimizerSceneConverterTest::inPlaceOptimizeOverdraw, + &MeshOptimizerSceneConverterTest::inPlaceOptimizeOverdraw, + &MeshOptimizerSceneConverterTest::inPlaceOptimizeOverdraw, + + &MeshOptimizerSceneConverterTest::inPlaceOptimizeVertexFetch, + &MeshOptimizerSceneConverterTest::inPlaceOptimizeVertexFetch, + &MeshOptimizerSceneConverterTest::inPlaceOptimizeVertexFetch, + &MeshOptimizerSceneConverterTest::inPlaceOptimizeVertexFetchNoAttributes, + + &MeshOptimizerSceneConverterTest::verbose, + &MeshOptimizerSceneConverterTest::verbose, + &MeshOptimizerSceneConverterTest::verbose, + &MeshOptimizerSceneConverterTest::verboseCustomAttribute, + &MeshOptimizerSceneConverterTest::verboseImplementationSpecificAttribute, + + &MeshOptimizerSceneConverterTest::inPlaceOptimizeEmpty, + &MeshOptimizerSceneConverterTest::inPlaceOptimizeEmpty, + &MeshOptimizerSceneConverterTest::inPlaceOptimizeEmpty, + + &MeshOptimizerSceneConverterTest::copy, + &MeshOptimizerSceneConverterTest::copyTriangleStrip2DPositions, + &MeshOptimizerSceneConverterTest::copyTriangleFanIndexed}); + + /* Load the plugin directly from the build tree. Otherwise it's static and + already loaded. */ + #ifdef MESHOPTIMIZERSCENECONVERTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT_OUTPUT(_manager.load(MESHOPTIMIZERSCENECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif +} + +void MeshOptimizerSceneConverterTest::notTriangles() { + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + + MeshData mesh{MeshPrimitive::Instances, 3}; + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convert(mesh)); + CORRADE_VERIFY(!converter->convertInPlace(mesh)); + CORRADE_COMPARE(out.str(), + "Trade::MeshOptimizerSceneConverter::convert(): expected a triangle mesh, got MeshPrimitive::Instances\n" + "Trade::MeshOptimizerSceneConverter::convertInPlace(): expected a triangle mesh, got MeshPrimitive::Instances\n"); +} + +void MeshOptimizerSceneConverterTest::notIndexed() { + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + + MeshData mesh{MeshPrimitive::Triangles, 3}; + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convert(mesh)); + CORRADE_VERIFY(!converter->convertInPlace(mesh)); + CORRADE_COMPARE(out.str(), + "Trade::MeshOptimizerSceneConverter::convert(): expected an indexed mesh\n" + "Trade::MeshOptimizerSceneConverter::convertInPlace(): expected an indexed mesh\n"); +} + +void MeshOptimizerSceneConverterTest::immutableIndexData() { + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + converter->configuration().setValue("optimizeVertexCache", true); + converter->configuration().setValue("optimizeOverdraw", false); + converter->configuration().setValue("optimizeVertexFetch", false); + + constexpr UnsignedByte indices[3]{}; + MeshData mesh{MeshPrimitive::Triangles, + {}, indices, MeshIndexData{indices}, 1}; + + CORRADE_VERIFY(converter->convert(mesh)); /* Here it's not a problem */ + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertInPlace(mesh)); + CORRADE_COMPARE(out.str(), + "Trade::MeshOptimizerSceneConverter::convertInPlace(): optimizeVertexCache, optimizeOverdraw and optimizeVertexFetch require index data to be mutable\n"); +} + +void MeshOptimizerSceneConverterTest::inPlaceOptimizeVertexFetchImmutableVertexData() { + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + converter->configuration().setValue("optimizeVertexCache", false); + converter->configuration().setValue("optimizeOverdraw", false); + converter->configuration().setValue("optimizeVertexFetch", true); + + Containers::Array indexData{3}; + MeshIndexData indices{MeshIndexType::UnsignedByte, indexData}; + constexpr UnsignedByte vertices[3]{}; + MeshData mesh{MeshPrimitive::Triangles, + std::move(indexData), indices, + {}, vertices, {}, 1}; + + CORRADE_VERIFY(converter->convert(mesh)); /* Here it's not a problem */ + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertInPlace(mesh)); + CORRADE_COMPARE(out.str(), + "Trade::MeshOptimizerSceneConverter::convertInPlace(): optimizeVertexFetch requires vertex data to be mutable\n"); +} + +void MeshOptimizerSceneConverterTest::inPlaceOptimizeVertexFetchNotInterleaved() { + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + converter->configuration().setValue("optimizeVertexCache", false); + converter->configuration().setValue("optimizeOverdraw", false); + converter->configuration().setValue("optimizeVertexFetch", true); + + Containers::Array indexData{3}; + MeshIndexData indices{MeshIndexType::UnsignedByte, indexData}; + Containers::Array vertexData{6}; + MeshData mesh{MeshPrimitive::Triangles, + std::move(indexData), indices, + std::move(vertexData), { + MeshAttributeData{meshAttributeCustom(0), VertexFormat::Byte, + 0, 3, 1}, + MeshAttributeData{meshAttributeCustom(1), VertexFormat::Byte, + 3, 3, 1}, + }, 3}; + + CORRADE_VERIFY(converter->convert(mesh)); /* Here it's not a problem */ + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convertInPlace(mesh)); + CORRADE_COMPARE(out.str(), + "Trade::MeshOptimizerSceneConverter::convertInPlace(): optimizeVertexFetch requires the mesh to be interleaved\n"); +} + +void MeshOptimizerSceneConverterTest::inPlaceOptimizeOverdrawNoPositions() { + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + converter->configuration().setValue("optimizeVertexCache", false); + converter->configuration().setValue("optimizeOverdraw", true); + converter->configuration().setValue("optimizeVertexFetch", false); + + Containers::Array indexData{3}; + MeshIndexData indices{MeshIndexType::UnsignedByte, indexData}; + MeshData mesh{MeshPrimitive::Triangles, + std::move(indexData), indices, + nullptr, {}, 1}; + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!converter->convert(mesh)); + CORRADE_VERIFY(!converter->convertInPlace(mesh)); + CORRADE_COMPARE(out.str(), + "Trade::MeshOptimizerSceneConverter::convert(): optimizeOverdraw requires the mesh to have positions\n" + "Trade::MeshOptimizerSceneConverter::convertInPlace(): optimizeOverdraw requires the mesh to have positions\n"); +} + +void MeshOptimizerSceneConverterTest::inPlaceOptimizeNone() { + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + converter->configuration().setValue("optimizeVertexCache", false); + converter->configuration().setValue("optimizeOverdraw", false); + converter->configuration().setValue("optimizeVertexFetch", false); + + const UnsignedInt indices[] { + 12, 13, 14, 15, 16, 12, 17, 18, 19, 17, 20, 21, 22, 23, 24, 22 + }; + + const Vector3 positionsOrNormals[] { + {0.0f, -0.525731f, 0.850651f}, + {0.850651f, 0.0f, 0.525731f}, + {0.850651f, 0.0f, -0.525731f}, + {-0.850651f, 0.0f, -0.525731f} + }; + + MeshData icosphere = Primitives::icosphereSolid(1); + CORRADE_COMPARE_AS(icosphere.indices().prefix(16), + Containers::arrayView(indices), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(icosphere.attribute(MeshAttribute::Position).prefix(4), + Containers::arrayView(positionsOrNormals), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(icosphere.attribute(MeshAttribute::Normal).prefix(4), + Containers::arrayView(positionsOrNormals), + TestSuite::Compare::Container); + + /* Make an immutable reference to verify that mutable data aren't required + when everything is disabled */ + MeshData icosphereImmutable{icosphere.primitive(), + {}, icosphere.indexData(), MeshIndexData{icosphere.indices()}, + {}, icosphere.vertexData(), Trade::meshAttributeDataNonOwningArray(icosphere.attributeData())}; + + /* This shouldn't change anything */ + CORRADE_VERIFY(converter->convertInPlace(icosphereImmutable)); + CORRADE_COMPARE_AS(icosphere.indices().prefix(16), + Containers::arrayView(indices), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(icosphere.attribute(MeshAttribute::Position).prefix(4), + Containers::arrayView(positionsOrNormals), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(icosphere.attribute(MeshAttribute::Normal).prefix(4), + Containers::arrayView(positionsOrNormals), + TestSuite::Compare::Container); +} + +template void MeshOptimizerSceneConverterTest::inPlaceOptimizeVertexCache() { + setTestCaseTemplateName(Math::TypeTraits::name()); + + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + converter->configuration().setValue("optimizeVertexCache", true); + converter->configuration().setValue("optimizeOverdraw", false); + converter->configuration().setValue("optimizeVertexFetch", false); + + /* Tried with a cubeSolid() first, but that one seems to have an optimal + layout already, hah. With 0 subdivisions the overdraw optimization does + nothing. */ + MeshData icosphere = MeshTools::compressIndices( + Primitives::icosphereSolid(1), + Implementation::meshIndexTypeFor()); + CORRADE_COMPARE(icosphere.indexType(), Implementation::meshIndexTypeFor()); + + CORRADE_VERIFY(converter->convertInPlace(icosphere)); + CORRADE_COMPARE_AS(icosphere.indices().prefix(16), Containers::arrayView({ + 12, 13, 14, 14, 13, 6, 6, 13, 25, 14, 6, 24, 22, 6, 25, 6 + }), TestSuite::Compare::Container); + + /* No change, same as in inPlaceOptimizeNone() */ + const Vector3 positionsOrNormals[]{ + {0.0f, -0.525731f, 0.850651f}, + {0.850651f, 0.0f, 0.525731f}, + {0.850651f, 0.0f, -0.525731f}, + {-0.850651f, 0.0f, -0.525731f} + }; + CORRADE_COMPARE_AS(icosphere.attribute(MeshAttribute::Position).prefix(4), + Containers::arrayView(positionsOrNormals), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(icosphere.attribute(MeshAttribute::Normal).prefix(4), + Containers::arrayView(positionsOrNormals), + TestSuite::Compare::Container); +} + +template void MeshOptimizerSceneConverterTest::inPlaceOptimizeOverdraw() { + setTestCaseTemplateName(Math::TypeTraits::name()); + + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + converter->configuration().setValue("optimizeVertexCache", true); + converter->configuration().setValue("optimizeOverdraw", true); + converter->configuration().setValue("optimizeVertexFetch", false); + + MeshData icosphere = MeshTools::compressIndices( + Primitives::icosphereSolid(1), + Implementation::meshIndexTypeFor()); + CORRADE_COMPARE(icosphere.indexType(), Implementation::meshIndexTypeFor()); + + /* The default 1.05 doesn't do anything */ + CORRADE_VERIFY(converter->convertInPlace(icosphere)); + CORRADE_COMPARE_AS(icosphere.indices().prefix(16), Containers::arrayView({ + 12, 13, 14, 14, 13, 6, 6, 13, 25, 14, 6, 24, 22, 6, 25, 6 + }), TestSuite::Compare::Container); + + /* Try again with a higher value */ + converter->configuration().setValue("optimizeVertexCache", false); + converter->configuration().setValue("optimizeOverdrawThreshold", 2.5f); + CORRADE_VERIFY(converter->convertInPlace(icosphere)); + CORRADE_COMPARE_AS(icosphere.indices().prefix(16), Containers::arrayView({ + 3, 17, 19, 3, 19, 31, 3, 30, 20, 31, 30, 3, 12, 13, 14, 14 + }), TestSuite::Compare::Container); + + /* No change, same as in inPlaceOptimizeNone() */ + const Vector3 positionsOrNormals[]{ + {0.0f, -0.525731f, 0.850651f}, + {0.850651f, 0.0f, 0.525731f}, + {0.850651f, 0.0f, -0.525731f}, + {-0.850651f, 0.0f, -0.525731f} + }; + CORRADE_COMPARE_AS(icosphere.attribute(MeshAttribute::Position).prefix(4), + Containers::arrayView(positionsOrNormals), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(icosphere.attribute(MeshAttribute::Normal).prefix(4), + Containers::arrayView(positionsOrNormals), + TestSuite::Compare::Container); +} + +template void MeshOptimizerSceneConverterTest::inPlaceOptimizeVertexFetch() { + setTestCaseTemplateName(Math::TypeTraits::name()); + + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + converter->configuration().setValue("optimizeVertexCache", true); + converter->configuration().setValue("optimizeOverdraw", true); + converter->configuration().setValue("optimizeVertexFetch", true); + + MeshData icosphere = MeshTools::compressIndices( + Primitives::icosphereSolid(1), + Implementation::meshIndexTypeFor()); + CORRADE_COMPARE(icosphere.indexType(), Implementation::meshIndexTypeFor()); + + CORRADE_VERIFY(converter->convertInPlace(icosphere)); + CORRADE_COMPARE_AS(icosphere.indices().prefix(16), Containers::arrayView({ + 0, 1, 2, 2, 1, 3, 3, 1, 4, 2, 3, 5, 6, 3, 4, 3 + }), TestSuite::Compare::Container); + + /* Gets reordered so the earliest values in the original index buffer are + early in memory also */ + const Vector3 positionsOrNormals[]{ + {1.0f, 0.0f, 0.0f}, + {0.809017f, 0.5f, -0.309017f}, + {0.809017f, 0.5f, 0.309017f}, + {0.525731f, 0.850651f, 0.0f} + }; + CORRADE_COMPARE_AS(icosphere.attribute(MeshAttribute::Position).prefix(4), + Containers::arrayView(positionsOrNormals), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(icosphere.attribute(MeshAttribute::Normal).prefix(4), + Containers::arrayView(positionsOrNormals), + TestSuite::Compare::Container); +} + +void MeshOptimizerSceneConverterTest::inPlaceOptimizeVertexFetchNoAttributes() { + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + converter->configuration().setValue("optimizeVertexCache", true); + converter->configuration().setValue("optimizeOverdraw", false); + converter->configuration().setValue("optimizeVertexFetch", true); + + MeshData icosphere = Primitives::icosphereSolid(1); + MeshIndexData indices{icosphere.indices()}; + MeshData icosphereIndicesOnly{icosphere.primitive(), + icosphere.releaseIndexData(), indices, icosphere.vertexCount()}; + + CORRADE_VERIFY(converter->convertInPlace(icosphereIndicesOnly)); + CORRADE_COMPARE_AS(icosphereIndicesOnly.indices().prefix(16), Containers::arrayView({ + /* Same as in inPlaceOptimizeVertexCache, as optimizeOverdraw would + need positions and optimizeVertexFetch is (silently) skipped because + there are no attribute data */ + 12, 13, 14, 14, 13, 6, 6, 13, 25, 14, 6, 24, 22, 6, 25, 6 + }), TestSuite::Compare::Container); +} + +template void MeshOptimizerSceneConverterTest::verbose() { + setTestCaseTemplateName(Math::TypeTraits::name()); + + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + converter->setFlags(SceneConverterFlag::Verbose); + + /* We need enough vertices for the optimization to make any difference, and + that unfortunately means 8-bit indices can't be verified here. Instead + it's done in verboseImplementationSpecificAttribute() below. */ + if(Implementation::meshIndexTypeFor() == MeshIndexType::UnsignedByte) + CORRADE_SKIP("The mesh is too large to fit into 8-bit indices."); + + MeshData icosphere = MeshTools::compressIndices( + Primitives::icosphereSolid(6), + Implementation::meshIndexTypeFor()); + CORRADE_COMPARE(icosphere.indexType(), Implementation::meshIndexTypeFor()); + + std::ostringstream out; + { + Debug redirectDebug{&out}; + CORRADE_VERIFY(converter->convert(icosphere)); + CORRADE_VERIFY(converter->convertInPlace(icosphere)); + } + CORRADE_COMPARE(out.str(), +R"(Trade::MeshOptimizerSceneConverter::convert(): processing stats: + vertex cache: + 165120 -> 58521 transformed vertices + 1 -> 1 executed warps + ACMR 2.01562 -> 0.714368 + ATVR 4.03105 -> 1.42867 + vertex fetch: + 3891008 -> 1582144 bytes fetched + overfetch 3.95794 -> 1.60936 + overdraw: + 308753 -> 308750 shaded pixels + 308748 -> 308748 covered pixels + overdraw 1.00002 -> 1.00001 +Trade::MeshOptimizerSceneConverter::convertInPlace(): processing stats: + vertex cache: + 165120 -> 58521 transformed vertices + 1 -> 1 executed warps + ACMR 2.01562 -> 0.714368 + ATVR 4.03105 -> 1.42867 + vertex fetch: + 3891008 -> 1582144 bytes fetched + overfetch 3.95794 -> 1.60936 + overdraw: + 308753 -> 308750 shaded pixels + 308748 -> 308748 covered pixels + overdraw 1.00002 -> 1.00001 +)"); + +} + +void MeshOptimizerSceneConverterTest::verboseCustomAttribute() { + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + converter->setFlags(SceneConverterFlag::Verbose); + /* All options on their defaults, should be the same as + optimizeVertexFetch() */ + + MeshData icosphere = Primitives::icosphereSolid(6); + MeshIndexData indices{icosphere.indices()}; + auto attributes = Containers::array({ + icosphere.attributeData(0), + /* Reinterpret the 12-byte normal as a Matrix3x2b[2] to verify vertex + fetch bytes are calculated correctly even for custom / matrix / + array attribs */ + Trade::MeshAttributeData{meshAttributeCustom(1), + VertexFormat::Matrix3x2bNormalized, 2, icosphere.attribute(1)} + }); + MeshData icosphereCustom{icosphere.primitive(), + icosphere.releaseIndexData(), indices, + icosphere.releaseVertexData(), std::move(attributes)}; + + std::ostringstream out; + { + Debug redirectDebug{&out}; + CORRADE_VERIFY(converter->convertInPlace(icosphereCustom)); + } + CORRADE_COMPARE(out.str(), +R"(Trade::MeshOptimizerSceneConverter::convertInPlace(): processing stats: + vertex cache: + 165120 -> 58521 transformed vertices + 1 -> 1 executed warps + ACMR 2.01562 -> 0.714368 + ATVR 4.03105 -> 1.42867 + vertex fetch: + 3891008 -> 1582144 bytes fetched + overfetch 3.95794 -> 1.60936 + overdraw: + 308753 -> 308750 shaded pixels + 308748 -> 308748 covered pixels + overdraw 1.00002 -> 1.00001 +)"); + +} + +void MeshOptimizerSceneConverterTest::verboseImplementationSpecificAttribute() { + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + converter->setFlags(SceneConverterFlag::Verbose); + + /* Using an 8-bit type to complement the verbose() test, which can't fit + into there */ + MeshData icosphere = MeshTools::compressIndices( + Primitives::icosphereSolid(1), + MeshIndexType::UnsignedByte); + + MeshIndexData indices{icosphere.indices()}; + auto attributes = Containers::array({ + icosphere.attributeData(0), + Trade::MeshAttributeData{icosphere.attributeName(1), + vertexFormatWrap(0x1234), icosphere.attribute(1)} + }); + MeshData icosphereExtra{icosphere.primitive(), + icosphere.releaseIndexData(), indices, + icosphere.releaseVertexData(), std::move(attributes)}; + + std::ostringstream out; + { + Debug redirectDebug{&out}; + Warning redirectWarning{&out}; + /** @todo test also convert() once the interleave() inside of it + isn't crashing on impl-specific vertex formats */ + CORRADE_VERIFY(converter->convertInPlace(icosphereExtra)); + } + CORRADE_COMPARE_AS(icosphereExtra.indices().prefix(16), + Containers::arrayView({ + /* Same as in inPlaceOptimizeVertexFetch() */ + 0, 1, 2, 2, 1, 3, 3, 1, 4, 2, 3, 5, 6, 3, 4, 3 + }), TestSuite::Compare::Container); + CORRADE_COMPARE(out.str(), +R"(Trade::MeshOptimizerSceneConverter::convertInPlace(): can't analyze vertex fetch for VertexFormat::ImplementationSpecific(0x1234) +Trade::MeshOptimizerSceneConverter::convertInPlace(): processing stats: + vertex cache: + 136 -> 49 transformed vertices + 1 -> 1 executed warps + ACMR 1.7 -> 0.6125 + ATVR 3.2381 -> 1.16667 + overdraw: + 149965 -> 149965 shaded pixels + 149965 -> 149965 covered pixels + overdraw 1 -> 1 +)"); +} + +template void MeshOptimizerSceneConverterTest::inPlaceOptimizeEmpty() { + setTestCaseTemplateName(Math::TypeTraits::name()); + + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + converter->setFlags(SceneConverterFlag::Verbose); + + MeshData icosphere = Primitives::icosphereSolid(0); + icosphere.releaseIndexData(); + icosphere.releaseVertexData(); + CORRADE_VERIFY(icosphere.isIndexed()); + CORRADE_COMPARE(icosphere.indexCount(), 0); + CORRADE_COMPARE(icosphere.vertexCount(), 0); + CORRADE_COMPARE(icosphere.attributeCount(), 2); + + /* It should simply do nothing (and it should especially not crash) */ + std::ostringstream out; + { + Debug redirectDebug{&out}; + CORRADE_VERIFY(converter->convertInPlace(icosphere)); + } + CORRADE_COMPARE(out.str(), +R"(Trade::MeshOptimizerSceneConverter::convertInPlace(): processing stats: + vertex cache: + 0 -> 0 transformed vertices + 0 -> 0 executed warps + ACMR 0 -> 0 + ATVR 0 -> 0 + vertex fetch: + 0 -> 0 bytes fetched + overfetch 0 -> 0 + overdraw: + 0 -> 0 shaded pixels + 0 -> 0 covered pixels + overdraw 0 -> 0 +)"); +} + +void MeshOptimizerSceneConverterTest::copy() { + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + + MeshData original = Primitives::icosphereSolid(1); + Containers::Optional optimized = converter->convert(original); + + CORRADE_VERIFY(optimized); + CORRADE_COMPARE(optimized->primitive(), original.primitive()); + CORRADE_COMPARE(optimized->indexCount(), original.indexCount()); + CORRADE_COMPARE(optimized->vertexCount(), original.vertexCount()); + CORRADE_COMPARE(optimized->attributeCount(), original.attributeCount()); + CORRADE_COMPARE(optimized->indexDataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_COMPARE(optimized->vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable); + + CORRADE_COMPARE_AS(optimized->indices().prefix(16), + Containers::arrayView({ + /* Same as in inPlaceOptimizeVertexFetch() */ + 0, 1, 2, 2, 1, 3, 3, 1, 4, 2, 3, 5, 6, 3, 4, 3 + }), TestSuite::Compare::Container); + + /* Same as in inPlaceOptimizeVertexFetch() */ + const Vector3 positionsOrNormals[]{ + {1.0f, 0.0f, 0.0f}, + {0.809017f, 0.5f, -0.309017f}, + {0.809017f, 0.5f, 0.309017f}, + {0.525731f, 0.850651f, 0.0f} + }; + CORRADE_COMPARE_AS(optimized->attribute(MeshAttribute::Position).prefix(4), + Containers::arrayView(positionsOrNormals), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(optimized->attribute(MeshAttribute::Normal).prefix(4), + Containers::arrayView(positionsOrNormals), + TestSuite::Compare::Container); +} + +void MeshOptimizerSceneConverterTest::copyTriangleStrip2DPositions() { + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + + /* Take a simple mesh just to verify it gets correctly converted to indexed + triangles; additionally it's 2D to check that the positions get expanded + to 3D internally */ + MeshData original = Primitives::squareSolid(); + Containers::Optional optimized = converter->convert(original); + + CORRADE_VERIFY(optimized); + CORRADE_COMPARE(optimized->primitive(), MeshPrimitive::Triangles); + CORRADE_COMPARE(optimized->indexCount(), 6); + CORRADE_COMPARE(optimized->vertexCount(), original.vertexCount()); + CORRADE_COMPARE(optimized->attributeCount(), original.attributeCount()); + CORRADE_COMPARE(optimized->indexDataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_COMPARE(optimized->vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable); + + CORRADE_COMPARE_AS(optimized->indices(), Containers::arrayView({ + 0, 1, 2, 2, 1, 3 + }), TestSuite::Compare::Container); + + CORRADE_COMPARE_AS(optimized->attribute(MeshAttribute::Position), + Containers::arrayView({ + { 1.0f, -1.0f}, + { 1.0f, 1.0f}, + {-1.0f, -1.0f}, + {-1.0f, 1.0f} + }), TestSuite::Compare::Container); +} + +void MeshOptimizerSceneConverterTest::copyTriangleFanIndexed() { + Containers::Pointer converter = _manager.instantiate("MeshOptimizerSceneConverter"); + + /* Take a circle (which is a fan) and add a trivial index buffer to it */ + MeshData original = Primitives::circle3DSolid(3); + const UnsignedByte indices[] { 0, 1, 2, 3, 4 }; + CORRADE_COMPARE(Containers::arraySize(indices), original.vertexCount()); + MeshData indexed{original.primitive(), + {}, indices, MeshIndexData{indices}, + {}, original.vertexData(), meshAttributeDataNonOwningArray(original.attributeData())}; + + Containers::Optional optimized = converter->convert(indexed); + + CORRADE_VERIFY(optimized); + CORRADE_COMPARE(optimized->primitive(), MeshPrimitive::Triangles); + CORRADE_COMPARE(optimized->indexCount(), 9); + CORRADE_COMPARE(optimized->vertexCount(), original.vertexCount()); + CORRADE_COMPARE(optimized->attributeCount(), original.attributeCount()); + CORRADE_COMPARE(optimized->indexDataFlags(), DataFlag::Owned|DataFlag::Mutable); + CORRADE_COMPARE(optimized->vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable); + + CORRADE_COMPARE_AS(optimized->indices(), Containers::arrayView({ + 0, 1, 2, 0, 2, 3, 0, 3, 4 + }), TestSuite::Compare::Container); + + CORRADE_COMPARE_AS(optimized->attribute(MeshAttribute::Position), + Containers::arrayView({ + {0.0f, 0.0f, 0.0f}, + {1.0f, 0.0f, 0.0f}, + {-0.5f, 0.866025f, 0.0f}, + {-0.5f, -0.866025f, 0.0f}, + {1.0f, 0.0f, 0.0f} + }), TestSuite::Compare::Container); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Trade::Test::MeshOptimizerSceneConverterTest) diff --git a/src/MagnumPlugins/MeshOptimizerSceneConverter/Test/configure.h.cmake b/src/MagnumPlugins/MeshOptimizerSceneConverter/Test/configure.h.cmake new file mode 100644 index 000000000..14f4637a2 --- /dev/null +++ b/src/MagnumPlugins/MeshOptimizerSceneConverter/Test/configure.h.cmake @@ -0,0 +1,26 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#cmakedefine MESHOPTIMIZERSCENECONVERTER_PLUGIN_FILENAME "${MESHOPTIMIZERSCENECONVERTER_PLUGIN_FILENAME}" diff --git a/src/MagnumPlugins/MeshOptimizerSceneConverter/configure.h.cmake b/src/MagnumPlugins/MeshOptimizerSceneConverter/configure.h.cmake new file mode 100644 index 000000000..7bf5994b1 --- /dev/null +++ b/src/MagnumPlugins/MeshOptimizerSceneConverter/configure.h.cmake @@ -0,0 +1,26 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#cmakedefine MAGNUM_MESHOPTIMIZERSCENECONVERTER_BUILD_STATIC diff --git a/src/MagnumPlugins/MeshOptimizerSceneConverter/importStaticPlugin.cpp b/src/MagnumPlugins/MeshOptimizerSceneConverter/importStaticPlugin.cpp new file mode 100644 index 000000000..f63349984 --- /dev/null +++ b/src/MagnumPlugins/MeshOptimizerSceneConverter/importStaticPlugin.cpp @@ -0,0 +1,35 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "MagnumPlugins/MeshOptimizerSceneConverter/configure.h" + +#ifdef MAGNUM_MESHOPTIMIZERSCENECONVERTER_BUILD_STATIC +#include + +static int magnumMeshOptimizerSceneConverterStaticImporter() { + CORRADE_PLUGIN_IMPORT(MeshOptimizerSceneConverter) + return 1; +} CORRADE_AUTOMATIC_INITIALIZER(magnumMeshOptimizerSceneConverterStaticImporter) +#endif