diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a1148e9c..f01c25226 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ option(WITH_FIND_MODULE "Install FindMagnum.cmake module into CMake's module dir # Parts of the library option(WITH_AUDIO "Build Audio library" OFF) option(WITH_DEBUGTOOLS "Build DebugTools library" ON) -cmake_dependent_option(WITH_MESHTOOLS "Build MeshTools library" ON "NOT WITH_DEBUGTOOLS" ON) +cmake_dependent_option(WITH_MESHTOOLS "Build MeshTools library" ON "NOT WITH_DEBUGTOOLS;NOT WITH_OBJIMPORTER" ON) cmake_dependent_option(WITH_PRIMITIVES "Builf Primitives library" ON "NOT WITH_DEBUGTOOLS" ON) cmake_dependent_option(WITH_SCENEGRAPH "Build SceneGraph library" ON "NOT WITH_DEBUGTOOLS;NOT WITH_SHAPES" ON) cmake_dependent_option(WITH_SHADERS "Build Shaders library" ON "NOT WITH_DEBUGTOOLS" ON) @@ -49,23 +49,35 @@ cmake_dependent_option(WITH_SHAPES "Build Shapes library" ON "NOT WITH_DEBUGTOOL option(WITH_TEXT "Build Text library" ON) cmake_dependent_option(WITH_TEXTURETOOLS "Build TextureTools library" ON "NOT WITH_TEXT;NOT WITH_DISTANCEFIELDCONVERTER" ON) -# Application libraries +# NaCl-specific application libraries if(CORRADE_TARGET_NACL) option(WITH_NACLAPPLICATION "Build NaClApplication library" OFF) cmake_dependent_option(WITH_WINDOWLESSNACLAPPLICATION "Build WindowlessNaClApplication library" OFF "NOT WITH_MAGNUMINFO" ON) -else() + +# Android-specific application libraries +elseif(CORRADE_TARGET_ANDROID) + option(WITH_ANDROIDAPPLICATION "Build AndroidApplication library" OFF) + +# X11, GLX and EGL-specific application libraries +elseif(CORRADE_TARGET_UNIX AND NOT APPLE) option(WITH_GLXAPPLICATION "Build GlxApplication library" OFF) cmake_dependent_option(WITH_WINDOWLESSGLXAPPLICATION "Build WindowlessGlxApplication library" OFF "NOT WITH_MAGNUMINFO;NOT WITH_DISTANCEFIELDCONVERTER" ON) cmake_dependent_option(WITH_XEGLAPPLICATION "Build XEglApplication library" OFF "TARGET_GLES" OFF) +endif() + +# Platform-independent (almost) application libraries +if(NOT CORRADE_TARGET_NACL AND NOT CORRADE_TARGET_ANDROID) cmake_dependent_option(WITH_GLUTAPPLICATION "Build GlutApplication library" OFF "NOT TARGET_GLES" OFF) option(WITH_SDL2APPLICATION "Build Sdl2Application library" OFF) endif() -# Utilities (currently depending on WindowlessGlxApplication) -if(UNIX OR CORRADE_TARGET_NACL) +# Magnum Info (currently only using GLX or NaCl) +if((CORRADE_TARGET_UNIX AND NOT APPLE) OR CORRADE_TARGET_NACL) option(WITH_MAGNUMINFO "Build magnum-info utility" OFF) endif() -if(UNIX) + +# Utilities (currently depending on WindowlessGlxApplication) +if(CORRADE_TARGET_UNIX AND NOT APPLE) cmake_dependent_option(WITH_FONTCONVERTER "Build magnum-fontconverter utility" OFF "NOT TARGET_GLES" OFF) cmake_dependent_option(WITH_DISTANCEFIELDCONVERTER "Build magnum-distancefieldconverter utility" OFF "NOT TARGET_GLES" OFF) endif() @@ -73,6 +85,7 @@ endif() # Plugins cmake_dependent_option(WITH_MAGNUMFONT "Build MagnumFont plugin" OFF "WITH_TEXT" OFF) cmake_dependent_option(WITH_MAGNUMFONTCONVERTER "Build MagnumFontConverter plugin" OFF "NOT MAGNUM_TARGET_GLES;WITH_TEXT" OFF) +option(WITH_OBJIMPORTER "Build ObjImporter plugin" OFF) cmake_dependent_option(WITH_TGAIMAGECONVERTER "Build TgaImageConverter plugin" OFF "NOT WITH_MAGNUMFONTCONVERTER" ON) cmake_dependent_option(WITH_TGAIMPORTER "Build TgaImporter plugin" OFF "NOT WITH_MAGNUMFONT" ON) cmake_dependent_option(WITH_WAVAUDIOIMPORTER "Build WavAudioImporter plugin" OFF "WITH_AUDIO" OFF) @@ -104,9 +117,15 @@ if(CORRADE_TARGET_NACL OR CORRADE_TARGET_EMSCRIPTEN) set(TARGET_GLES2 1) endif() +# If targeting Android, set explicit OpenGL ES support. Decision between 2.0 +# and 3.0 is up to the user +if(CORRADE_TARGET_ANDROID) + set(TARGET_GLES 1) +endif() + # NaCl newlib toolchain supports only static linking, dynamic linking is -# meaningless on Emscripten -if(CORRADE_TARGET_NACL_NEWLIB OR CORRADE_TARGET_EMSCRIPTEN) +# meaningless on Emscripten and too inconvenient on Android +if(CORRADE_TARGET_NACL_NEWLIB OR CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) set(BUILD_STATIC ON) endif() @@ -123,7 +142,7 @@ else() find_package(OpenGLES3 REQUIRED) endif() -# Configuration variables (saved later to corradeConfigure.h) +# Configuration variables (saved later to configure.h) if(TARGET_GLES) set(MAGNUM_TARGET_GLES 1) if(TARGET_GLES2) @@ -132,6 +151,9 @@ if(TARGET_GLES) set(MAGNUM_TARGET_GLES3 1) endif() endif() +if(CORRADE_TARGET_EMSCRIPTEN) + set(MAGNUM_TARGET_WEBGL 1) +endif() if(TARGET_DESKTOP_GLES) set(MAGNUM_TARGET_DESKTOP_GLES 1) endif() @@ -156,12 +178,18 @@ endif() include(CorradeLibSuffix) set(MAGNUM_BINARY_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/bin) set(MAGNUM_LIBRARY_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) -set(MAGNUM_PLUGINS_INSTALL_DIR ${MAGNUM_LIBRARY_INSTALL_DIR}/magnum) -set(MAGNUM_PLUGINS_FONT_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/fonts) -set(MAGNUM_PLUGINS_FONTCONVERTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/fontconverters) -set(MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/imageconverters) -set(MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/importers) -set(MAGNUM_PLUGINS_AUDIOIMPORTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/audioimporters) +set(MAGNUM_PLUGINS_DEBUG_INSTALL_DIR ${MAGNUM_LIBRARY_INSTALL_DIR}/magnum-d) +set(MAGNUM_PLUGINS_RELEASE_INSTALL_DIR ${MAGNUM_LIBRARY_INSTALL_DIR}/magnum) +set(MAGNUM_PLUGINS_FONT_DEBUG_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_INSTALL_DIR}/fonts) +set(MAGNUM_PLUGINS_FONT_RELEASE_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_INSTALL_DIR}/fonts) +set(MAGNUM_PLUGINS_FONTCONVERTER_DEBUG_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_INSTALL_DIR}/fontconverters) +set(MAGNUM_PLUGINS_FONTCONVERTER_RELEASE_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_INSTALL_DIR}/fontconverters) +set(MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_INSTALL_DIR}/imageconverters) +set(MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_INSTALL_DIR}/imageconverters) +set(MAGNUM_PLUGINS_IMPORTER_DEBUG_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_INSTALL_DIR}/importers) +set(MAGNUM_PLUGINS_IMPORTER_RELEASE_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_INSTALL_DIR}/importers) +set(MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_INSTALL_DIR}/audioimporters) +set(MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_INSTALL_DIR}/audioimporters) set(MAGNUM_DATA_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/magnum) set(MAGNUM_CMAKE_FIND_MODULE_INSTALL_DIR ${CMAKE_ROOT}/Modules) set(MAGNUM_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include/Magnum) diff --git a/Doxyfile b/Doxyfile index 542a114ab..ec1dd4e2d 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1653,7 +1653,19 @@ INCLUDE_FILE_PATTERNS = # instead of the = operator. PREDEFINED = DOXYGEN_GENERATING_OUTPUT \ - MAGNUM_BUILD_DEPRECATED CORRADE_DEPRECATED(message)= + MAGNUM_BUILD_DEPRECATED CORRADE_DEPRECATED(message)= \ + MAGNUM_EXPORT= \ + MAGNUM_AUDIO_EXPORT= \ + MAGNUM_DEBUGTOOLS_EXPORT= \ + MAGNUM_MATH_EXPORT= \ + MAGNUM_MESHTOOLS_EXPORT= \ + MAGNUM_PLATFORM_EXPORT= \ + MAGNUM_PRIMITIVES_EXPORT= \ + MAGNUM_SCENEGRAPH_EXPORT= \ + MAGNUM_SHADERS_EXPORT= \ + MAGNUM_SHAPES_EXPORT= \ + MAGNUM_TEXT_EXPORT= \ + MAGNUM_TEXTURETOOLS_EXPORT= # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. diff --git a/README.md b/README.md index 8b17a853b..68cfeae81 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ Platforms: GLUT or SDL2 toolkit) * **Windows** (through GLUT or SDL2 toolkit) * **OS X** (through SDL2 toolkit, thanks to [Miguel Martin](https://github.com/miguelishawt)) +* **Android** 2.3 (API Level 9) and higher * **Google Chrome** (through [Native Client](https://developers.google.com/native-client/), both `newlib` and `glibc` toolchains are supported) * **HTML5/JavaScript** (through [Emscripten](https://github.com/kripken/emscripten/wiki)) diff --git a/doc/best-practices.dox b/doc/best-practices.dox index b1f17c9a0..3079ebf12 100644 --- a/doc/best-practices.dox +++ b/doc/best-practices.dox @@ -54,21 +54,22 @@ information. - [Best Practices for Working with Texture Data](http://developer.apple.com/library/ios/#documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/TechniquesForWorkingWithTextureData/TechniquesForWorkingWithTextureData.html) - [Best Practices for Shaders](http://developer.apple.com/library/ios/#documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/BestPracticesforShaders/BestPracticesforShaders.html#//apple_ref/doc/uid/TP40008793-CH7-SW3) -@subsection best-practices-nacl Google Chrome Native Client +@subsection best-practices-webgl WebGL (Emscripten) -- [Best practices for 3D graphics](https://developers.google.com/native-client/beta/devguide/coding/3D-graphics#best-practices) +WebGL is subset of OpenGL ES 2.0 with some [specific restrictions and features](http://www.khronos.org/registry/webgl/specs/latest/1.0/#6), namely requirement +for unique buffer target binding, aligned buffer offset and stride and some +other restrictions and also support for combined depth/stencil buffer +attachments. See @ref Buffer, @ref Framebuffer, @ref Texture::setSubImage() "*Texture::setSubImage()", +@ref Mesh::addVertexBuffer(), @ref Renderer::setStencilFunction(), +@ref Renderer::setStencilMask() and @ref Renderer::setBlendFunction() +documentation for more information. -@subsection best-practices-web-buffer-types Native Client and Emscripten require unique buffer binding +@subsection best-practices-nacl Google Chrome Native Client -As noted in the above link, buffers in NaCl implementation and and also in -WebGL need to be bound only to one unique target, i.e., @ref Buffer bound to -@ref Buffer::Target::Array cannot be later rebound to @ref Buffer::Target::ElementArray. -However, %Magnum by default uses any sufficient target when binding the buffer -internally (e.g. for setting data or copying). To avoid this, set target hint -to desired target, either in constructor or using @ref Buffer::setTargetHint(). +- [Best practices for 3D graphics](https://developers.google.com/native-client/beta/devguide/coding/3D-graphics#best-practices) -To ease up the development, @ref Mesh checks proper target hint when adding -vertex and index buffers in both Native Client and Emscripten. +Similarly to WebGL, buffers in NaCl implementation need to be bound only to one +unique target. See @ref Buffer class documentation for more information. @section best-practices-hw Hardware-specific diff --git a/doc/building.dox b/doc/building.dox index 16d6bf68f..0146387d2 100644 --- a/doc/building.dox +++ b/doc/building.dox @@ -69,7 +69,7 @@ assuming you have at least basic knowledge of CMake. On Unix-based OSs, the library (for example with support for GLUT applications) can be built and installed using these four commands: - mkdir -p build && cd build + mkdir build && cd build cmake .. \ -DCMAKE_INSTALL_PREFIX=/usr \ -DWITH_GLUTAPPLICATION=ON @@ -102,8 +102,7 @@ The most straightforward way to build and install the library is again via the command-line. The bonus point is that you don't even need to wait for Visual Studio to load: - mkdir build - cd build + mkdir build && cd build cmake -DCMAKE_INSTALL_PREFIX="C:/Sys" .. cmake --build . cmake --build . --target install @@ -135,6 +134,14 @@ plan to use them with shared libraries later, enable also position-independent code with `BUILD_STATIC_PIC`. If you want to build with another compiler (e.g. Clang), pass `-DCMAKE_CXX_COMPILER=clang++` to CMake. +Libraries and static plugins built in `Debug` configuration (e.g. with +`CMAKE_BUILD_TYPE` set to `Debug`) have `-d` suffix to make it possible to have +both debug and release libraries installed alongside each other. *Dynamic* +plugins in `Debug` configuration are installed to `magnum-d` subdirectory +instead of `magnum`. Headers and other files are the same for both. The library +and plugin distinction is handled semi-automatically when using %Magnum in +depending projects, see @ref cmake for more information. + %Magnum by default does not install `FindMagnum.cmake`, as you should bundle the module with your code instead of depending on it being in system location. You can install it by enabling `WITH_FIND_MODULE`. @@ -186,6 +193,7 @@ None of the @ref Platform "application libraries" is built by default (and you need at least one). Choose the one which suits your requirements and your platform best: +- `WITH_ANDROIDAPPLICATION` - @ref Platform::AndroidApplication "AndroidApplication" - `WITH_GLUTAPPLICATION` - @ref Platform::GlutApplication "GlutApplication" - `WITH_GLXAPPLICATION` - @ref Platform::GlxApplication "GlxApplication" - `WITH_NACLAPPLICATION` - @ref Platform::NaClApplication "NaClApplication" @@ -216,6 +224,7 @@ default. - `WITH_MAGNUMFONTCONVERTER` -- @ref Text::MagnumFontConverter "MagnumFontConverter" plugin. Available only if `WITH_TEXT` is enabled. Enables also building of @ref Trade::TgaImageConverter "TgaImageConverter" plugin. +- `WITH_OBJIMPORTER` -- @ref Trade::ObjImporter "ObjImporter" plugin. - `WITH_TGAIMPORTER` -- @ref Trade::TgaImporter "TgaImporter" plugin. - `WITH_TGAIMAGECONVERTER` -- @ref Trade::TgaImageConverter "TgaImageConverter" plugin. @@ -317,8 +326,9 @@ contents in `toolchains/` subdirectory. @subsection building-cross-win Crosscompiling for Windows using MinGW @note This guide is tailored mainly for crosscompiling from ArchLinux. For -this system there is also prepared `mingw32-magnum` development package in -root, named `PKGBUILD-mingw32`. + this system there is also prepared `mingw32-magnum` development package in + `package/archlinux`, named `PKGBUILD-mingw32`. See + @ref building-packages-arch "above" for more information. You will need MinGW32 versions of the compiler and all dependent libraries (Corrade), i.e. these ArchLinux packages: @@ -327,19 +337,18 @@ You will need MinGW32 versions of the compiler and all dependent libraries - `mingw32-runtime` - `mingw32-corrade` -Then create build directory and run cmake and make. You may need to modify the -`basic-mingw32.cmake` file and `CMAKE_INSTALL_PREFIX` to suit your distribution -filesystem hierarchy. +Then create build directory and run cmake and build command in it. You may need +to modify the `basic-mingw32.cmake` file and `CMAKE_INSTALL_PREFIX` to suit +your distribution filesystem hierarchy. - mkdir build-win - cd build-win + mkdir build-win && cd build-win cmake .. \ -DCMAKE_TOOLCHAIN_FILE=../toolchains/archlinux/basic-mingw32.cmake \ -DCMAKE_INSTALL_PREFIX=/usr/i486-mingw32 - make + cmake --build . -Then you can install the package using `make install` to make it available for -depending projects. +Then you can install the package using `cmake --build . --target install` to +make it available for depending projects. @subsection building-cross-nacl Crosscompiling for Google Chrome Native Client @@ -354,14 +363,13 @@ adapt `NACL_PREFIX` variable in `generic/NaCl-*-x86-32.cmake` and to find the compiler. NaCl currently supports only OpenGL ES 2, thus `TARGET_GLES` and `TARGET_GLES2` is always enabled. -Then create build directories for x86-32 and x86-64 and run cmake and make in -them. The toolchains need access to the platform file, so be sure to properly -set **absolute** path to `modules/` directory containing `Platform/NaCl.cmake`. -Also adapt `CMAKE_INSTALL_PREFIX` to the same value as in `NACL_PREFIX` in -toolchain file. +Then create build directories for x86-32 and x86-64 and run cmake and build +command in them. The toolchains need access to the platform file, so be sure to +properly set **absolute** path to `modules/` directory containing +`Platform/NaCl.cmake`. Also adapt `CMAKE_INSTALL_PREFIX` to the same value as +in `NACL_PREFIX` in toolchain file. - mkdir -p build-nacl-x86-32 - cd build-nacl-x86-32 + mkdir build-nacl-x86-32 && cd build-nacl-x86-32 cmake .. \ -DCMAKE_MODULE_PATH="/absolute/path/to/toolchains/modules" \ -DCMAKE_TOOLCHAIN_FILE="../toolchains/generic/NaCl-newlib-x86-32.cmake" \ @@ -369,23 +377,24 @@ toolchain file. -DCMAKE_INSTALL_PREFIX=/usr/nacl \ -DWITH_NACLAPPLICATION=ON \ -DLIB_SUFFIX=/32 - make + cmake --build . - mkdir -p build-nacl-x86-64 - cd build-nacl-x86-64 + mkdir build-nacl-x86-64 && cd build-nacl-x86-64 cmake .. \ -DCMAKE_MODULE_PATH="/absolute/path/to/toolchains/modules" \ -DCMAKE_TOOLCHAIN_FILE="../toolchains/generic/NaCl-newlib-x86-64.cmake" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr/nacl \ -DWITH_NACLAPPLICATION=ON - make + cmake --build . -Then you can install both versions using `make install` to make them available -for depending projects. The headers are shared by both versions. +Then you can install both versions using `cmake --build . --target install` to +make them available for depending projects. The headers are shared by both +versions. -For ArchLinux there are also prepared package files in root, named -`PKGBUILD-nacl-glibc` and `PKGBUILD-nacl-newlib`. +For ArchLinux there are also prepared package files in `package/archlinux`, +named `PKGBUILD-nacl-glibc` and `PKGBUILD-nacl-newlib`, see +@ref building-packages-arch "above" for more information. @subsection building-cross-emscripten Crosscompiling for Emscripten @@ -397,34 +406,81 @@ to path where Emscripten is installed. Default is `/usr/emscripten`. Emscripten supports dynamic libraries only to simplify porting and they are generally slower, thus `BUILD_STATIC` is implicitly enabled. -Then create build directory and run cmake and make in it. The toolchain needs -access to its platform file, so be sure to properly set **absolute** path to -`modules/` directory containing `Platform/Emscripten.cmake`. Also set -`CMAKE_INSTALL_PREFIX` to value which is contained in `CMAKE_FIND_ROOT_PATH` in -toolchain file. +Then create build directory and run cmake and build command in it. The +toolchain needs access to its platform file, so be sure to properly set +**absolute** path to `modules/` directory containing `Platform/Emscripten.cmake`. +Also set `CMAKE_INSTALL_PREFIX` to path contained in `EMSCRIPTEN_TOOLCHAIN_PATH`. - mkdir -p build-emscripten - cd build-emscripten + mkdir build-emscripten && cd build-emscripten cmake .. \ -DCMAKE_MODULE_PATH="/absolute/path/to/toolchains/modules" \ -DCMAKE_TOOLCHAIN_FILE="../toolchains/generic/Emscripten.cmake" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr/emscripten/system \ -DWITH_SDL2APPLICATION=ON - make + cmake --build . -Then you can install the library using `make install` to make it available for -depending projects. +Then you can install the library using `cmake --build . --target install` to +make it available for depending projects. If you have Node.js installed, you can also build and run unit tests using `ctest`. See `BUILD_TESTS` above. +For ArchLinux there is also prepared package file in `package/archlinux`, +named `PKGBUILD-emscripten`, see @ref building-packages-arch "above" for more +information. + +@subsection building-cross-android Crosscompiling for Android ARM and x86 + +You will need [Android NDK](https://developer.android.com/tools/sdk/ndk/index.html) +installed and configured. + +Don't forget to adapt `ANDROID_NDK_ROOT` in `generic/Android-*.cmake` to path +where NDK is installed. Default is `/opt/android-ndk`. Adapt also +`ANDROID_SYSROOT` to your preferred API level. You might also need to update +`ANDROID_TOOLCHAIN_PREFIX` and `ANDROID_TOOLCHAIN_ROOT` to fit your system. + +Then create build directory and run cmake and build command in it. The +toolchain needs access to its platform file, so be sure to properly set **absolute** +path to `modules/` directory containing `Platform/Android.cmake`. Also set +`CMAKE_INSTALL_PREFIX` to `/usr` subdirectory of `ANDROID_SYSROOT`. + +Note that `BUILD_STATIC` is implicitly enabled, because manually loading all +depending shared libraries using JNI would be too inconvenient. Decision +between OpenGL ES 2.0 and ES 3.0 is left up to the user (i.e. you need to set +`TARGET_GLES2` to `ON` or `OFF`). + + mkdir build-android-arm && cd build-android-arm + cmake .. \ + -DCMAKE_MODULE_PATH="/absolute/path/to/toolchains/modules" \ + -DCMAKE_TOOLCHAIN_FILE="../toolchains/generic/Android-ARM.cmake" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/opt/android-ndk/platforms/android-19/arch-arm/usr \ + -DTARGET_GLES=ON -DTARGET_GLES2=ON + cmake --build . + + mkdir build-android-x86 && cd build-android-x86 + cmake .. \ + -DCMAKE_MODULE_PATH="/absolute/path/to/toolchains/modules" \ + -DCMAKE_TOOLCHAIN_FILE="../toolchains/generic/Android-x86.cmake" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/opt/android-ndk/platforms/android-19/arch-x86/usr \ + -DTARGET_GLES=ON -DTARGET_GLES2=ON + cmake --build . + +Then you can install the library using `cmake --build . --target install` to +make it available for depending projects. + +For ArchLinux there are also prepared package files in `package/archlinux`, +named `PKGBUILD-android-arm` and `PKGBUILD-android-x86`, see +@ref building-packages-arch "above" for more information. + @section building-ci-jenkins Jenkins Continuous Integration In `package/ci/` there are `jenkins.xml` and `jenkins-gltests.xml` files containing job configuration, one for build and non-GL tests and the other for -GL tests only. Setup your Jenkins server, enable the **Git** and -**Text-finder** plugin and download the CLI application from here: +GL tests only. Setup your Jenkins server, enable the **Git** and **Text-finder** +plugin and download the CLI application from here: http://your-jenkins-server/cli diff --git a/doc/cmake.dox b/doc/cmake.dox index c1b60c6e9..d5b7aa086 100644 --- a/doc/cmake.dox +++ b/doc/cmake.dox @@ -51,17 +51,19 @@ variables: - `MAGNUM_FOUND` -- Whether the library was found - `MAGNUM_LIBRARIES` -- %Magnum library and dependent libraries - `MAGNUM_INCLUDE_DIRS` -- Root include dir and include dirs of dependencies -- `MAGNUM_PLUGINS_DIR` -- Base directory with plugins, defaults to `magnum/` - subdirectory of dir where Magnum library was found. You can modify it (e.g. - set it to `.` when deploying on Windows with plugins stored relatively to - the executable), the following `MAGNUM_PLUGINS_*_DIR` variables depend on - it. -- `MAGNUM_PLUGINS_FONT_DIR` -- Directory with font plugins -- `MAGNUM_PLUGINS_FONTCONVERTER_DIR` -- Directory with font converter plugins -- `MAGNUM_PLUGINS_IMAGECONVERTER_DIR` -- Directory with image converter +- `MAGNUM_PLUGINS_DIR` -- Base directory with dynamic plugins, defaults to + `magnum/` subdirectory of dir where Magnum library was found (or + `magnum-d/` in debug build). . You can modify it (e.g. set it to `.` when + deploying on Windows with plugins stored relatively to the executable), + the following `MAGNUM_PLUGINS_*_DIR` variables depend on it. +- `MAGNUM_PLUGINS_FONT_DIR` -- Directory with dynamic font plugins +- `MAGNUM_PLUGINS_FONTCONVERTER_DIR` -- Directory with dynamic font converter + plugins +- `MAGNUM_PLUGINS_IMAGECONVERTER_DIR` -- Directory with dynamic image + converter plugins +- `MAGNUM_PLUGINS_IMPORTER_DIR` -- Directory with dynamic importer plugins +- `MAGNUM_PLUGINS_AUDIOIMPORTER_DIR` -- Directory with dynamic audio importer plugins -- `MAGNUM_PLUGINS_IMPORTER_DIR` -- Directory with importer plugins -- `MAGNUM_PLUGINS_AUDIOIMPORTER_DIR` -- Directory with audio importer plugins However, this command will try to find only the base library, not the optional components. The base library depends on %Corrade and OpenGL libraries (or @@ -103,6 +105,8 @@ dependencies, you need to find the dependency and then link to it. `%Text` component and `TgaImporter` plugin) - `MagnumFontConverter` -- @ref Text::MagnumFontConverter "MagnumFontConverter" plugin (depends on `%Text` component and `%TgaImageConverter` plugin) +- `ObjImporter` -- @ref Trade::ObjImporter "ObjImporter" plugin (depends on + `%MeshTools` component) - `TgaImageConverter` -- @ref Trade::TgaImageConverter "TgaImageConverter" plugin - `TgaImporter` -- @ref Trade::TgaImporter "TgaImporter" plugin @@ -130,6 +134,17 @@ convenience aliases `MAGNUM_APPLICATION_LIBRARIES` / `MAGNUM_WINDOWLESSAPPLICATION_LIBRARIES` and `MAGNUM_APPLICATION_INCLUDE_DIRS` / `MAGNUM_WINDOWLESSAPPLICATION_INCLUDE_DIRS` to simplify porting. +The package is found if either debug or release version of each requested +library (or plugin) is found. If both debug and release libraries (or plugins) +are found, proper version is chosen based on actual build configuration of the +project (i.e. `Debug` build is linked to debug libraries, `Release` build to +release libraries). Note that this autodetection might fail for the +`MAGNUM_PLUGINS_DIR` variable, i.e. you might need to switch it manually to +`magnum-d/` or `magnum/` subdirectory based on whether you want to dynamically +load plugins with or without debug information. You can also make use of +`CMAKE_BUILD_TYPE` or `CMAKE_CFG_INTDIR` CMake variables for compile-time +decision. + Features of found %Magnum library are exposed in these CMake variables, they are also available as preprocessor variables if including Magnum.h: diff --git a/doc/mainpage.dox b/doc/mainpage.dox index 7e83a9135..920ad37cb 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -78,6 +78,7 @@ Platforms: GLUT or SDL2 toolkit) - **Windows** (through GLUT or SDL2 toolkit) - **OS X** (through SDL2 toolkit, thanks to [Miguel Martin](https://github.com/miguelishawt)) +- **Android** 2.3 (API Level 9) and higher - **Google Chrome** (through [Native Client](https://developers.google.com/native-client/), both `newlib` and `glibc` toolchains are supported) - **HTML5/JavaScript** (through [Emscripten](https://github.com/kripken/emscripten/wiki)) diff --git a/doc/matrix-vector.dox b/doc/matrix-vector.dox index 595cc43bc..d57326320 100644 --- a/doc/matrix-vector.dox +++ b/doc/matrix-vector.dox @@ -77,13 +77,14 @@ Vector3i b; // zero-filled Matrix3 identity; // diagonal set to 1 Matrix3 zero(Matrix::Zero); // zero-filled -Color4 black1; // {0.0f, 0.0f, 0.0f, 1.0f} -BasicColor4 black2; // {0, 0, 0, 255} +Color4 black1; // {0.0f, 0.0f, 0.0f, 1.0f} +Color4ub black2; // {0, 0, 0, 255} @endcode Most common and most efficient way to create vector is to pass all values to constructor, matrix is created by passing all column vectors to the -constructor. +constructor. All constructors check number of passed arguments and the errors +are catched at compile time. @code Vector3i vec(0, 1, 2); @@ -91,11 +92,9 @@ Matrix3 mat({0.0f, 1.9f, 2.2f}, {3.5f, 4.0f, 5.1f}, {6.0f, 7.3f, 8.0f}); @endcode -All constructors check number of passed arguments and the errors are catched -at compile time. -You can specify all components of vector or whole diagonal of square matrix at -once or you can create diagonal matrix from vector: +You can specify all components of vector or whole diagonal of square matrix +with single value or create diagonal matrix from vector: @code Matrix3 diag(Matrix3::Identity, 2.0f); // diagonal set to 2.0f, zeros elsewhere Vector3i fill(10); // {10, 10, 10} @@ -121,20 +120,14 @@ Int[] mat = { 2, 4, 6, 1, 3, 5 }; Math::Matrix2x3::from(mat) *= 2; // mat == { 4, 8, 12, 2, 6, 10 } @endcode -Note that unlike constructors, this function has no way to check whether the -array is long enough to contain all elements, so use with caution. -You can also *explicitly* convert between data types: -@code -Vector4 floating(1.3f, 2.7f, -15.0f, 7.0f); -auto integral = Vector4i(floating); // {1, 2, -15, 7} -@endcode +Note that, unlike constructors, this function has no way to check whether the +array is long enough to contain all elements, so use with caution. @section matrix-vector-component-access Accessing matrix and vector components Column vectors of matrices and vector components can be accessed using square -brackets, there is also round bracket operator for accessing matrix components -directly: +brackets: @code Matrix3x2 a; a[2] /= 2.0f; // third column (column major indexing, see explanation below) @@ -145,7 +138,7 @@ b[1] = 1; // second element @endcode Row vectors can be accessed too, but only for reading, and the access is slower -due to the way the matrix is stored (see explanation below): +due to the way the matrix is stored (see @ref matrix-vector-column-major "explanation below"): @code Vector2i c = a.row(2); // third row @endcode @@ -170,6 +163,38 @@ Vector4i bgra = swizzle<'b', 'g', 'r', 'a'>(original); // { 3, 2, -1, 4 } Math::Vector<6, Int> w10xyz = swizzle<'w', '1', '0', 'x', 'y', 'z'>(original); // { 4, 1, 0, -1, 2, 3 } @endcode +@section matrix-vector-conversion Converting between different underlying types + +All vector, matrix and other classes in @ref Math namespace and also +@ref Color3 and @ref Color4 classes are able to be constructed from type with +different underlying type (e.g. convert between integer and floating-point or +betweeen @ref Float and @ref Double). Unlike with plain C++ data types, the +conversion is done via *explicit* constructor. That might sound inconvenient, +but doing the conversion explicitly avoids common issues like precision loss +(or, on the other hand, doing computations in unnecessarily high precision). + +To further emphasise the intent of conversion (so it doesn't look like accident +or typo), you are encouraged to use `auto b = Type{a}` instead of `Type b{a}`. +@code +Vector3 a{2.2f, 0.25f, -5.1f}; +//Vector3i b = a; // error, implicit conversion not allowed +auto c = Vector3i{a}; // {2, 0, -5} +auto d = Vector3d{a}; // {2.2, 0.25, -5.1} +@endcode + +For normalizing and denormalizing there are @ref Math::normalize() and +@ref Math::denormalize() functions: +@code +Color3 a{0.8f, 1.0f, 0.3f}; +auto b = Math::denormalize(a); // {204, 255, 76} + +Color3ub c{64, 127, 89}; +auto d = Math::normalize(c); // {0.251, 0.498, 0.349} +@endcode + +See @ref matrix-vector-componentwise "below" for more information about other +available component-wise operations. + @section matrix-vector-operations Operations with matrices and vectors Vectors can be added, subtracted, negated and multiplied or divided with @@ -193,8 +218,8 @@ In %Magnum all mulitplication/division operations involving integral vectors will have integral result, you need to convert both arguments to the same floating-point type to have floating-point result. @code -BasicColor3 color(80, 116, 34); -BasicColor3 lighter = color*1.5f; // lighter = {120, 174, 51} +Color3ub color(80, 116, 34); +Color3ub lighter = color*1.5f; // lighter = {120, 174, 51} Vector3i a(4, 18, -90); Vector3 multiplier(2.2f, 0.25f, 0.1f); @@ -230,6 +255,63 @@ Math::RectangularMatrix<4, 1, Float> d; Matrix4x3 e = b*d; @endcode +@section matrix-vector-componentwise Component-wise and inter-vector operations + +As shown above, vectors can be added and multiplied component-wise using the +`+` or `*` operator. You can use @ref Math::Vector::sum() "sum()" and +@ref Math::Vector::product() "product()" for sum or product of components in +one vector: +@code +Float a = Vector3{1.5f, 0.3f, 8.0f}.sum(); // 8.8f +Int b = Vector3i{32, -5, 7}.product() // 1120 +@endcode + +Component-wise minimum and maximum of two vectors can be done using +@ref Math::min(), @ref Math::max() or @ref Math::minmax(), similarly with +@ref Vector::min() "min()", @ref Vector::max() "max()" and +@ref Vector2::minmax() "minmax()" for components in one vector. +@code +Vector3i a{-5, 7, 24}; +Vector3i b{8, -2, 12}; + +Vector3i min = Math::min(a, b); // {-5, -2, 12} +Int max = a.max(); // 24 +@endcode + +The vectors can be also compared component-wise, the result is returned in +@ref Math::BoolVector class: +@code +BoolVector<3> largerOrEqual = a >= b; // {false, true, true} +bool anySmaller = (a < b).any(); // true +bool allLarger = (a > b).all(); // false +@endcode + +There are also function for component-wise rounding, sign operations, square +root, various interpolation and (de)normalization functionality: +@code +Vector3 a{5.5f, -0.3f, 75.0f}; +Vector3 b = Math::round(a); // {5.0f, 0.0f, 75.0f} +Vector3 c = Math::abs(a); // {5.5f, -0.3f, 75.0f} +Vector3 d = Math::clamp(a, -0.2f, 55.0f); // {5.5f, -0.2f, 55.0f} +@endcode + +Component-wise functions are implemented only for vectors and not for matrices +to keep the math library in sane and maintainable size. Instead, you can +reinterpret the matrix as vector and do the operation on it (and vice versa): +@code +Matrix3x2 mat; +Math::Vector<6, Float> vec = mat.toVector(); +// ... +mat = Matrix3x2::fromVector(vec); +@endcode + +Note that all component-wise functions in Math namespace work also for scalars: +@code +std::pair minmax = Math::minmax(24, -5); // -5, 24 +Int a = Math::lerp(0, 360, 0.75f); // 270 +auto b = Math::denormalize(0.89f); // 226 +@endcode + @section matrix-vector-column-major Matrices are column-major and vectors are columns OpenGL matrices are column-major, thus it is reasonable to have matrices in diff --git a/doc/opengl-mapping.dox b/doc/opengl-mapping.dox index c855e4bb8..7d77dc62f 100644 --- a/doc/opengl-mapping.dox +++ b/doc/opengl-mapping.dox @@ -62,8 +62,7 @@ OpenGL function | Matching API @fn_gl{BindRenderbuffer} | not needed, handhled internally in @ref Renderbuffer @fn_gl{BindSampler} | | @fn_gl{BindSamplers} | | -@fn_gl{BindTexture}, \n @fn_gl_extension{BindMultiTexture,EXT,direct_state_access} | @ref AbstractTexture::bind() -@fn_gl{BindTextures} | | +@fn_gl{BindTexture}, \n @fn_gl{BindTextures}, \n @fn_gl_extension{BindMultiTexture,EXT,direct_state_access} | @ref AbstractTexture::bind() @fn_gl{BindTransformFeedback} | | @fn_gl{BindVertexArray} | not needed, handhled internally in @ref Mesh @fn_gl{BindVertexBuffer} | | diff --git a/doc/opengl-support.dox b/doc/opengl-support.dox index f103f292b..409d5e9d3 100644 --- a/doc/opengl-support.dox +++ b/doc/opengl-support.dox @@ -71,7 +71,7 @@ following: @extension{EXT,texture_shared_exponent} | done @extension{EXT,framebuffer_sRGB} | | @extension{EXT,draw_buffers2} | | -@extension{EXT,texture_integer} | missing integer color specification functions +@extension{EXT,texture_integer} | done (GL 3.0 subset) @extension{EXT,transform_feedback} | | @extension{NV,half_float} | done (GL 3.0 subset) @extension{NV,depth_buffer_float} | | @@ -100,7 +100,7 @@ following: @extension{ARB,provoking_vertex} | done @extension{ARB,seamless_cube_map} | done @extension{ARB,sync} | | -@extension{ARB,texture_multisample} | | +@extension{ARB,texture_multisample} | missing sample location queries and sample mask @extension{ARB,vertex_array_bgra} | done @subsection opengl-support-33 OpenGL 3.3 @@ -187,7 +187,7 @@ following: @extension{ARB,stencil_texturing} | | @extension{ARB,texture_buffer_range} | done @extension{ARB,texture_query_levels} | done (shading language only) -@extension{ARB,texture_storage_multisample} | | +@extension{ARB,texture_storage_multisample} | done @extension{ARB,texture_view} | | @extension{ARB,vertex_attrib_binding} | | @@ -201,7 +201,7 @@ following: @extension{ARB,buffer_storage} | | @extension{ARB,clear_texture} | | @extension{ARB,enhanced_layouts} | done (shading language only) -@extension{ARB,multi_bind} | | +@extension{ARB,multi_bind} | only texture binding @extension{ARB,query_buffer_object} | | @extension{ARB,texture_mirror_clamp_to_edge} | done @extension{ARB,texture_stencil8} | done @@ -222,6 +222,7 @@ following: @extension{EXT,texture_filter_anisotropic} (also in ES) | done @extension{EXT,texture_mirror_clamp} | only GL 4.4 subset @extension{EXT,direct_state_access} | done for implemented functionality +@extension{EXT,shader_integer_mix} (also in ES) | done (shading language only) @extension2{EXT,debug_label} (also in ES) | missing pipeline, transform feedback and sampler label @extension2{EXT,debug_marker} (also in ES) | missing marker groups @extension{GREMEDY,string_marker} | done @@ -244,6 +245,7 @@ supported. @es_extension{ANGLE,framebuffer_multisample} | done @es_extension{ANGLE,depth_texture} | done @es_extension{APPLE,framebuffer_multisample} | done (ES 3.0 subset) +@es_extension{APPLE,texture_max_level} | done @es_extension{ARM,rgba8} | done @es_extension{EXT,texture_type_2_10_10_10_REV} | done @es_extension{EXT,discard_framebuffer} | done @@ -324,7 +326,8 @@ add any performance gains, is not supported in %Magnum. See also with serious performance drops. Multisampling is far superior solution. - Fixed precision data types (`GL_FIXED` in OpenGL ES) are not supported, as they occupy the same memory as floats and they aren't faster than floats on - current hardware anymore. + current hardware anymore. They are also not available in WebGL or desktop + GL. - Shader compiler is assumed to be present (`GL_SHADER_COMPILER` returning true), as all desktop GL implementations and also ES3 are required to support it. diff --git a/doc/platform.dox b/doc/platform.dox index 607ffc2d0..44c6700a5 100644 --- a/doc/platform.dox +++ b/doc/platform.dox @@ -66,10 +66,10 @@ blue color is shown in the following code listing. repository. @code -#include -#include -#include -#include +#include +#include +#include +#include using namespace Magnum; @@ -88,7 +88,7 @@ MyApplication::MyApplication(const Arguments& arguments): Platform::Application( void MyApplication::drawEvent() { // Clear the window - defaultFramebuffer.clear(DefaultFramebuffer::Clear::Color); + defaultFramebuffer.clear(FramebufferClear::Color); // The context is double-buffered, swap buffers swapBuffers(); @@ -148,8 +148,8 @@ renderer string and exits is in the following code listing. repository. @code -#include -#include +#include +#include using namespace Magnum; diff --git a/doc/portability.dox b/doc/portability.dox index 1f7cd2644..578d0fa46 100644 --- a/doc/portability.dox +++ b/doc/portability.dox @@ -40,9 +40,10 @@ format is not supported. If you include @ref Magnum.h, you get these predefined macros: -- @ref MAGNUM_TARGET_GLES if targeting OpenGL ES -- @ref MAGNUM_TARGET_GLES2 if targeting OpenGL ES 2.0 -- @ref MAGNUM_TARGET_GLES3 if targeting OpenGL ES 3.0 +- @ref MAGNUM_TARGET_GLES if targeting OpenGL ES +- @ref MAGNUM_TARGET_GLES2 if targeting OpenGL ES 2.0 +- @ref MAGNUM_TARGET_GLES3 if targeting OpenGL ES 3.0 +- @ref MAGNUM_TARGET_WEBGL if targeting WebGL Example usage: @code @@ -57,7 +58,7 @@ Renderer::setPolygonMode(Renderer::PolygonMode::Lines); Each feature is marked accordingly if it is not available in some targets. See also @ref requires-gl, @ref requires-gles20 and @ref requires-gles30. -@section portability-compiler Compiler-specific code +@section portability-compiler Compiler- and platform-specific code %Magnum is attempting to be future-proof and as intuitive for users as possible. Many features from C++11 are used to simplify things and make them @@ -73,6 +74,10 @@ platform) which compiler your code will support, code written for e.g. GCC 4.6 will work also on Magnum compiled with support for newer compilers, although newer compilers may catch errors that weren't spotted by earlier versions. +Some functionality (such as dynamic plugin loading or filesystem access) might +not be available on particular platforms. @ref Corrade.h contains defintions +which you can use for platform-aware code. + @section portability-extensions Extension-aware code Some functionality is depending on support of particular extension and thus @@ -156,8 +161,8 @@ if(!Context::instance()->isExtensionSupported(object, {{}, 23.0f}); +Shapes::ShapeGroup3D shapes; +Object3D& a; +auto aShape = new Shapes::Shape(a, {{}, 23.0f}, &shapes); + +Object3D& b; +auto bShape = new Shapes::Shape(b, {{1.0f, 0.2f, 3.0f}}, &shapes); + +// Translate point so the objects no longer collide +shapes.setClean(); +if(aShape->collides(*bShape)) { + const Shapes::Collision3D c = aShape->collision(*bShape); + b.translate(c.separationNormal()*c.separationDistance()); +} @endcode -See also @ref scenegraph for introduction. +There is also @ref Shapes::ShapeGroup::firstCollision() function which returns +arbitrary first collision for given shape in whole group (or `nullptr`, if +there isn't any collision). + +You can also use @ref DebugTools::ShapeRenderer to visualize the shapes for +debugging purposes. See also @ref scenegraph for introduction. - Previous page: @ref scenegraph - Next page: @ref debug-tools diff --git a/doc/types.dox b/doc/types.dox index ba8ff3af0..027c89e66 100644 --- a/doc/types.dox +++ b/doc/types.dox @@ -87,21 +87,82 @@ underlying type. Any super- or sub-class of the same size and underlying type can be used equivalently (e.g. @ref Math::Vector or @ref Color3 instead of @ref Vector3). +@section types-binary Binary representation + +Scalar types with GLSL equivalent are verified to be exactly the same as +corresponding `GL*` types. Matrix and vector classes have the same binary +representation as corresponding array of numeric values without any additional +data or padding (e.g. `sizeof(Vector3i) == sizeof(Int[3])`), all matrices are +stored in column-major order. + +This means that all scalar, matrix and vector types can be used directly for +filling GPU buffers and textures without any need for data extraction or +conversion. For convenience all vector and matrix classes provide +@ref Math::RectangularMatrix::data() "data()" function, which returns pointer +to the internal data array. + +@section types-special Special types + +%Magnum has special type for strongly-typed representation of angles, namely +the @ref Deg and @ref Rad classes (or @ref Degd and @ref Radd with @ref Double +as underlying type). Their only purpose is to avoid common degree-vs-radian +bugs (i.e. entering degree value where radians should be) and make the +conversion between these two representations easier. They are just a tiny +`inline` `constexpr` wrapper around the native type and they support all +meaningful numeric operations, so using them won't have any performance or +usability impact in practice. + +These classes are *not* implicitly constructible or convertible from/to +@ref Float or @ref Double, you have to either construct/convert them explicitly +or use custom `_degf`/`_deg` and `_radf`/`_rad` literals: +@code +//Deg a = 60.0f // error, no implicit conversion from Float +Deg a = 60.0_degf; // okay + +Float b = 3.2831853f; +auto tau = Rad{b} + 3.0_radf; +Radd pi = 3.141592653589793_rad; + +//Double c = pi; // error, no implicit conversion to Double +auto c = Double{pi}; // okay +@endcode + +They can be implicitly converted to each other, but conversion to different +underlying type is *explicit* to avoid precision loss (or, on the other hand, +unnecessarily high precision) during computations: +@code +Rad d = 60.0_degf; // 1.0471976f +auto e = Degd{pi}; // 180.0 + +//Rad f = pi; // error, no implicit conversion of underlying types +auto f = Rad{pi}; // 3.141592654f +@endcode + +These classes are used exclusively in all functions taking and returning angles +-- trigonometry, angle computation, rotating transformation etc. Thanks to +implicit conversion you can seamlessly use either radians or degrees without +any need to care about what input the function expects: +@code +Float a = Math::sin(1.32457_radf); +Complex b = Complex::rotation(60.0_degf); +@endcode + @section types-other Other types Other types, which don't have their GLSL equivalent, are: - @ref Complex or @ref Complexd, @ref DualComplex or @ref DualComplexd -- @ref Quaternion or @ref Quaterniond, @ref DualQuaternion or @ref DualQuaterniond -- @ref Range1D / @ref Range2D / @ref Range3D, @ref Range1Di / @ref Range2Di / @ref Range3Di or - @ref Range1Dd / @ref Range2Dd / @ref Range3Dd +- @ref Quaternion or @ref Quaterniond, @ref DualQuaternion or + @ref DualQuaterniond +- @ref Range1D / @ref Range2D / @ref Range3D, @ref Range1Di / @ref Range2Di / + @ref Range3Di or @ref Range1Dd / @ref Range2Dd / @ref Range3Dd These types can be used in GLSL either by extracting values from their underlying structure or converting them to types supported by GLSL (e.g. quaternion to matrix). -For your convenience, there is also alias for class with often used constants -- -@ref Constants or @ref Constantsd. +For your convenience, there is also alias for class with often used constants +-- @ref Constants or @ref Constantsd. - Previous page: @ref platform - Next page: @ref matrix-vector diff --git a/modules/FindCorrade.cmake b/modules/FindCorrade.cmake index dfb568e5c..1ec78ce83 100644 --- a/modules/FindCorrade.cmake +++ b/modules/FindCorrade.cmake @@ -3,7 +3,7 @@ # Basic usage: # find_package(Corrade [REQUIRED]) # This module tries to find Corrade library and then defines: -# CORRADE_FOUND - True if Corrade library is found +# CORRADE_FOUND - True if Corrade is found # CORRADE_INCLUDE_DIR - Root include dir # CORRADE_INTERCONNECT_LIBRARIES - Interconnect library and dependent # libraries @@ -14,9 +14,14 @@ # CORRADE_TESTSUITE_LIBRARIES - TestSuite library and dependent # libraries # CORRADE_RC_EXECUTABLE - Resource compiler executable +# The package is found if either debug or release version of each library is +# found. If both debug and release libraries are found, proper version is +# chosen based on actual build configuration of the project (i.e. Debug build +# is linked to debug libraries, Release build to release libraries). +# # Corrade configures the compiler to use C++11 standard. Additionally you can -# use CORRADE_CXX_FLAGS to enable additional pedantic set of warnings and enable -# hidden visibility by default. +# use CORRADE_CXX_FLAGS to enable additional pedantic set of warnings and +# enable hidden visibility by default. # # Features of found Corrade library are exposed in these variables: # CORRADE_GCC47_COMPATIBILITY - Defined if compiled with compatibility @@ -42,6 +47,7 @@ # CORRADE_TARGET_NACL_GLIBC - Defined if compiled for Google Chrome # Native Client with `glibc` toolchain # CORRADE_TARGET_EMSCRIPTEN - Defined if compiled for Emscripten +# CORRADE_TARGET_ANDROID - Defined if compiled for Android # # If CORRADE_BUILD_DEPRECATED is defined, the CORRADE_INCLUDE_DIR variable also # contains path directly to Corrade directory (i.e. for includes without @@ -71,15 +77,15 @@ # add_executable(app source1 source2 ... ${app_resources}) # # Add dynamic plugin. -# corrade_add_plugin(plugin_name install_dir metadata_file -# sources...) +# corrade_add_plugin(plugin_name debug_install_dir release_install_dir +# metadata_file sources...) # The macro adds preprocessor directive CORRADE_DYNAMIC_PLUGIN. Additional # libraries can be linked in via target_link_libraries(plugin_name ...). If -# install_dir is set to CMAKE_CURRENT_BINARY_DIR (e.g. for testing purposes), -# the files are copied directly, without the need to run `make install`. Note -# that the files are actually put into configuration-based subdirectory, i.e. -# ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}. See documentation of -# CMAKE_CFG_INTDIR variable for more information. +# debug_install_dir is set to CMAKE_CURRENT_BINARY_DIR (e.g. for testing +# purposes), the files are copied directly, without the need to perform install +# step. Note that the files are actually put into configuration-based +# subdirectory, i.e. ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}. See +# documentation of CMAKE_CFG_INTDIR variable for more information. # # # Add static plugin. @@ -88,7 +94,11 @@ # The macro adds preprocessor directive CORRADE_STATIC_PLUGIN. Additional # libraries can be linked in via target_link_libraries(plugin_name ...). If # install_dir is set to CMAKE_CURRENT_BINARY_DIR (e.g. for testing purposes), -# no installation is performed. +# no installation rules are added. +# +# Note that plugins built in debug configuration (e.g. with CMAKE_BUILD_TYPE +# set to Debug) have "-d" suffix to make it possible to have both debug and +# release plugins installed alongside each other. # # # Additionally these variables are defined for internal usage: @@ -98,6 +108,9 @@ # CORRADE_PLUGINMANAGER_LIBRARY - Plugin manager library (w/o # dependencies) # CORRADE_TESTSUITE_LIBRARY - TestSuite library (w/o dependencies) +# CORRADE_*_LIBRARY_DEBUG - Debug version of given library, if found +# CORRADE_*_LIBRARY_RELEASE - Release version of given library, if +# found # # @@ -126,10 +139,28 @@ # # Libraries -find_library(CORRADE_INTERCONNECT_LIBRARY CorradeInterconnect) -find_library(CORRADE_UTILITY_LIBRARY CorradeUtility) -find_library(CORRADE_PLUGINMANAGER_LIBRARY CorradePluginManager) -find_library(CORRADE_TESTSUITE_LIBRARY CorradeTestSuite) +foreach(_component Interconnect Utility PluginManager TestSuite) + string(TOUPPER ${_component} _COMPONENT) + + # Try to find both debug and release version + find_library(CORRADE_${_COMPONENT}_LIBRARY_DEBUG Corrade${_component}-d) + find_library(CORRADE_${_COMPONENT}_LIBRARY_RELEASE Corrade${_component}) + + # Set the _LIBRARY variable based on what was found + if(CORRADE_${_COMPONENT}_LIBRARY_DEBUG AND CORRADE_${_COMPONENT}_LIBRARY_RELEASE) + set(CORRADE_${_COMPONENT}_LIBRARY + debug ${CORRADE_${_COMPONENT}_LIBRARY_DEBUG} + optimized ${CORRADE_${_COMPONENT}_LIBRARY_RELEASE}) + elseif(CORRADE_${_COMPONENT}_LIBRARY_DEBUG) + set(CORRADE_${_COMPONENT}_LIBRARY ${CORRADE_${_COMPONENT}_LIBRARY_DEBUG}) + elseif(CORRADE_${_COMPONENT}_LIBRARY_RELEASE) + set(CORRADE_${_COMPONENT}_LIBRARY ${CORRADE_${_COMPONENT}_LIBRARY_RELEASE}) + endif() + + mark_as_advanced(CORRADE_${_COMPONENT}_LIBRARY_DEBUG + CORRADE_${_COMPONENT}_LIBRARY_RELEASE + CORRADE_${_COMPONENT}_LIBRARY) +endforeach() # RC executable find_program(CORRADE_RC_EXECUTABLE corrade-rc) @@ -219,6 +250,10 @@ string(FIND "${_corradeConfigure}" "#define CORRADE_TARGET_EMSCRIPTEN" _TARGET_E if(NOT _TARGET_EMSCRIPTEN EQUAL -1) set(CORRADE_TARGET_EMSCRIPTEN 1) endif() +string(FIND "${_corradeConfigure}" "#define CORRADE_TARGET_ANDROID" _TARGET_ANDROID) +if(NOT _TARGET_ANDROID EQUAL -1) + set(CORRADE_TARGET_ANDROID 1) +endif() set(CORRADE_UTILITY_LIBRARIES ${CORRADE_UTILITY_LIBRARY}) set(CORRADE_INTERCONNECT_LIBRARIES ${CORRADE_INTERCONNECT_LIBRARY} ${CORRADE_UTILITY_LIBRARIES}) @@ -230,11 +265,12 @@ if(CORRADE_TARGET_UNIX OR CORRADE_TARGET_NACL_GLIBC) set(CORRADE_PLUGINMANAGER_LIBRARIES ${CORRADE_PLUGINMANAGER_LIBRARIES} ${CMAKE_DL_LIBS}) endif() -mark_as_advanced(CORRADE_UTILITY_LIBRARY - CORRADE_INTERCONNECT_LIBRARY - CORRADE_PLUGINMANAGER_LIBRARY - CORRADE_TESTSUITE_LIBRARY - _CORRADE_INCLUDE_DIR +# AndroidLogStreamBuffer class needs to be linked to log library +if(CORRADE_TARGET_ANDROID) + set(CORRADE_UTILITY_LIBRARIES ${CORRADE_UTILITY_LIBRARIES} log) +endif() + +mark_as_advanced(_CORRADE_INCLUDE_DIR _CORRADE_MODULE_DIR) # Add Corrade dir to include path if this is deprecated build diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake index 998911a3a..68462478b 100644 --- a/modules/FindMagnum.cmake +++ b/modules/FindMagnum.cmake @@ -7,16 +7,19 @@ # MAGNUM_LIBRARIES - Magnum library and dependent libraries # MAGNUM_INCLUDE_DIRS - Root include dir and include dirs of # dependencies -# MAGNUM_PLUGINS_DIR - Base directory with plugins, defaults to -# `magnum/` subdirectory of dir where Magnum library was found. You can -# modify it (e.g. set it to `.` when deploying on Windows with plugins -# stored relatively to the executable), the following MAGNUM_PLUGINS_*_DIR -# variables depend on it. -# MAGNUM_PLUGINS_FONT_DIR - Directory with font plugins -# MAGNUM_PLUGINS_FONTCONVERTER_DIR - Directory with font converter plugins -# MAGNUM_PLUGINS_IMAGECONVERTER_DIR - Directory with image converter plugins -# MAGNUM_PLUGINS_IMPORTER_DIR - Directory with importer plugins -# MAGNUM_PLUGINS_AUDIOIMPORTER_DIR - Directory with audio importer plugins +# MAGNUM_PLUGINS_DIR - Base directory with dynamic plugins, defaults +# to magnum/ subdirectory of dir where Magnum library was found (or magnum-d/ +# in debug build). You can modify it (e.g. set it to `.` when deploying on +# Windows with plugins stored relatively to the executable), the following +# MAGNUM_PLUGINS_*_DIR variables depend on it. +# MAGNUM_PLUGINS_FONT_DIR - Directory with dynamic font plugins +# MAGNUM_PLUGINS_FONTCONVERTER_DIR - Directory with dynamic font converter +# plugins +# MAGNUM_PLUGINS_IMAGECONVERTER_DIR - Directory with dynamic image converter +# plugins +# MAGNUM_PLUGINS_IMPORTER_DIR - Directory with dynamic importer plugins +# MAGNUM_PLUGINS_AUDIOIMPORTER_DIR - Directory with dynamic audio importer +# plugins # This command will try to find only the base library, not the optional # components. The base library depends on Corrade and OpenGL libraries (or # OpenGL ES libraries). Additional dependencies are specified by the @@ -35,6 +38,7 @@ # and TgaImporter plugin) # MagnumFontConverter - Magnum bitmap font converter plugin (depends on Text # component and TgaImageConverter plugin) +# ObjImporter - OBJ importer plugin # TgaImageConverter - TGA image converter plugin # TgaImporter - TGA importer plugin # WavAudioImporter - WAV audio importer plugin (depends on Audio component) @@ -58,6 +62,17 @@ # MAGNUM_WINDOWLESSAPPLICATION_LIBRARIES and MAGNUM_APPLICATION_INCLUDE_DIRS # / MAGNUM_WINDOWLESSAPPLICATION_INCLUDE_DIRS to simplify porting. # +# The package is found if either debug or release version of each requested +# library (or plugin) is found. If both debug and release libraries (or +# plugins) are found, proper version is chosen based on actual build +# configuration of the project (i.e. Debug build is linked to debug libraries, +# Release build to release libraries). Note that this autodetection might fail +# for the MAGNUM_PLUGINS_DIR variable, i.e. you might need to switch it +# manually to magnum-d/ or magnum/ subdirectory based on whether you want +# to dynamically load plugins with or without debug information. You can also +# make use of CMAKE_BUILD_TYPE or CMAKE_CFG_INTDIR CMake variables for +# compile-time decision. +# # Features of found Magnum library are exposed in these variables: # MAGNUM_BUILD_DEPRECATED - Defined if compiled with deprecated APIs # included @@ -74,29 +89,29 @@ # plugins (i.e. instead of `MagnumPlugins/` prefix). # # Additionally these variables are defined for internal usage: -# MAGNUM_INCLUDE_DIR - Root include dir (w/o -# dependencies) -# MAGNUM_LIBRARY - Magnum library (w/o -# dependencies) -# MAGNUM_*_LIBRARY - Component libraries (w/o -# dependencies) -# MAGNUM_LIBRARY_INSTALL_DIR - Library installation directory -# MAGNUM_PLUGINS_INSTALL_DIR - Plugin installation directory -# MAGNUM_PLUGINS_FONT_INSTALL_DIR - Font plugin installation -# directory -# MAGNUM_PLUGINS_FONTCONVERTER_INSTALL_DIR - Font converter plugin -# installation directory -# MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR - Image converter plugin -# installation directory -# MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR - Importer plugin installation +# MAGNUM_INCLUDE_DIR - Root include dir (w/o dependencies) +# MAGNUM_LIBRARY - Magnum library (w/o dependencies) +# MAGNUM_LIBRARY_DEBUG - Debug version of Magnum library, if found +# MAGNUM_LIBRARY_RELEASE - Release version of Magnum library, if found +# MAGNUM_*_LIBRARY - Component libraries (w/o dependencies) +# MAGNUM_*_LIBRARY_DEBUG - Debug version of given library, if found +# MAGNUM_*_LIBRARY_RELEASE - Release version of given library, if found +# MAGNUM_LIBRARY_INSTALL_DIR - Library installation directory +# MAGNUM_PLUGINS_[DEBUG|RELEASE]_INSTALL_DIR - Plugin installation directory +# MAGNUM_PLUGINS_FONT_[DEBUG|RELEASE]_INSTALL_DIR - Font plugin installation # directory -# MAGNUM_PLUGINS_AUDIOIMPORTER_INSTALL_DIR - Audio omporter plugin +# MAGNUM_PLUGINS_FONTCONVERTER_[DEBUG|RELEASE]_INSTALL_DIR - Font converter +# plugin installation directory +# MAGNUM_PLUGINS_IMAGECONVERTER_[DEBUG|RELEASE]_INSTALL_DIR - Image converter +# plugin installation directory +# MAGNUM_PLUGINS_IMPORTER_[DEBUG|RELEASE]_INSTALL_DIR - Importer plugin # installation directory -# MAGNUM_CMAKE_FIND_MODULE_INSTALL_DIR - Installation dir for CMake -# Find* modules -# MAGNUM_INCLUDE_INSTALL_DIR - Header installation directory -# MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR - Plugin header installation -# directory +# MAGNUM_PLUGINS_AUDIOIMPORTER_[DEBUG|RELEASE]_INSTALL_DIR - Audio importer +# plugin installation directory +# MAGNUM_CMAKE_FIND_MODULE_INSTALL_DIR - Installation dir for CMake Find* +# modules +# MAGNUM_INCLUDE_INSTALL_DIR - Header installation directory +# MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR - Plugin header installation directory # # @@ -127,8 +142,29 @@ # Dependencies find_package(Corrade REQUIRED) -# Magnum library -find_library(MAGNUM_LIBRARY Magnum) +# Base Magnum library +find_library(MAGNUM_LIBRARY_DEBUG Magnum-d) +find_library(MAGNUM_LIBRARY_RELEASE Magnum) + +# Set the MAGNUM_LIBRARY variable based on what was found, use that information +# to guess also build type of dynamic plugins +if(MAGNUM_LIBRARY_DEBUG AND MAGNUM_LIBRARY_RELEASE) + set(MAGNUM_LIBRARY + debug ${MAGNUM_LIBRARY_DEBUG} + optimized ${MAGNUM_LIBRARY_RELEASE}) + get_filename_component(_MAGNUM_LIBRARY_PATH ${MAGNUM_LIBRARY_DEBUG} PATH) + # TODO: how to handle this with MSVC and other multi-configuration tools? + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(_MAGNUM_PLUGINS_DIR_SUFFIX "-d") + endif() +elseif(MAGNUM_LIBRARY_DEBUG) + set(MAGNUM_LIBRARY ${MAGNUM_LIBRARY_DEBUG}) + get_filename_component(_MAGNUM_LIBRARY_PATH ${MAGNUM_LIBRARY_DEBUG} PATH) + set(_MAGNUM_PLUGINS_DIR_SUFFIX "-d") +elseif(MAGNUM_LIBRARY_RELEASE) + set(MAGNUM_LIBRARY ${MAGNUM_LIBRARY_RELEASE}) + get_filename_component(_MAGNUM_LIBRARY_PATH ${MAGNUM_LIBRARY_RELEASE} PATH) +endif() # Root include dir find_path(MAGNUM_INCLUDE_DIR @@ -162,10 +198,22 @@ if(NOT _TARGET_DESKTOP_GLES EQUAL -1) set(MAGNUM_TARGET_DESKTOP_GLES 1) endif() +# Dependent libraries and includes +set(MAGNUM_INCLUDE_DIRS ${MAGNUM_INCLUDE_DIR} + ${MAGNUM_INCLUDE_DIR}/MagnumExternal/OpenGL + ${CORRADE_INCLUDE_DIR}) +set(MAGNUM_LIBRARIES ${MAGNUM_LIBRARY} + ${CORRADE_UTILITY_LIBRARIES} + ${CORRADE_PLUGINMANAGER_LIBRARIES}) if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES) find_package(OpenGL REQUIRED) -else() + set(MAGNUM_LIBRARIES ${MAGNUM_LIBRARIES} ${OPENGL_gl_LIBRARY}) +elseif(MAGNUM_TARGET_GLES2) find_package(OpenGLES2 REQUIRED) + set(MAGNUM_LIBRARIES ${MAGNUM_LIBRARIES} ${OPENGLES2_LIBRARY}) +elseif(MAGNUM_TARGET_GLES3) + find_package(OpenGLES3 REQUIRED) + set(MAGNUM_LIBRARIES ${MAGNUM_LIBRARIES} ${OPENGLES3_LIBRARY}) endif() # On Windows and in static builds, *Application libraries need to have @@ -225,8 +273,30 @@ foreach(component ${Magnum_FIND_COMPONENTS}) # break something else set(_tmp_prefixes ${CMAKE_FIND_LIBRARY_PREFIXES}) set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES} "") + find_library(MAGNUM_${_COMPONENT}_LIBRARY ${component} PATH_SUFFIXES magnum/${_MAGNUM_${_COMPONENT}_PATH_SUFFIX}) + + # Try to find both debug and release version. Dynamic and static debug + # libraries are on different places. + find_library(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG ${component} + PATH_SUFFIXES magnum-d/${_MAGNUM_${_COMPONENT}_PATH_SUFFIX}) + find_library(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG ${component}-d + PATH_SUFFIXES magnum/${_MAGNUM_${_COMPONENT}_PATH_SUFFIX}) + find_library(MAGNUM_${_COMPONENT}_LIBRARY_RELEASE ${component} + PATH_SUFFIXES magnum/${_MAGNUM_${_COMPONENT}_PATH_SUFFIX}) + + # Set the _LIBRARY variable based on what was found + if(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG AND MAGNUM_${_COMPONENT}_LIBRARY_RELEASE) + set(MAGNUM_${_COMPONENT}_LIBRARY + debug ${MAGNUM_${_COMPONENT}_LIBRARY_DEBUG} + optimized ${MAGNUM_${_COMPONENT}_LIBRARY_RELEASE}) + elseif(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG) + set(MAGNUM_${_COMPONENT}_LIBRARY ${MAGNUM_${_COMPONENT}_LIBRARY_DEBUG}) + elseif(MAGNUM_${_COMPONENT}_LIBRARY_RELEASE) + set(MAGNUM_${_COMPONENT}_LIBRARY ${MAGNUM_${_COMPONENT}_LIBRARY_RELEASE}) + endif() + set(CMAKE_FIND_LIBRARY_PREFIXES ${_tmp_prefixes}) # Set library defaults, find the library @@ -234,13 +304,37 @@ foreach(component ${Magnum_FIND_COMPONENTS}) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX Magnum/${component}) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES ${component}.h) - find_library(MAGNUM_${_COMPONENT}_LIBRARY Magnum${component}) + # Try to find both debug and release version + find_library(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG Magnum${component}-d) + find_library(MAGNUM_${_COMPONENT}_LIBRARY_RELEASE Magnum${component}) + + # Set the _LIBRARY variable based on what was found + if(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG AND MAGNUM_${_COMPONENT}_LIBRARY_RELEASE) + set(MAGNUM_${_COMPONENT}_LIBRARY + debug ${MAGNUM_${_COMPONENT}_LIBRARY_DEBUG} + optimized ${MAGNUM_${_COMPONENT}_LIBRARY_RELEASE}) + elseif(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG) + set(MAGNUM_${_COMPONENT}_LIBRARY ${MAGNUM_${_COMPONENT}_LIBRARY_DEBUG}) + elseif(MAGNUM_${_COMPONENT}_LIBRARY_RELEASE) + set(MAGNUM_${_COMPONENT}_LIBRARY ${MAGNUM_${_COMPONENT}_LIBRARY_RELEASE}) + endif() endif() # Applications if(${component} MATCHES .+Application) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX Magnum/Platform) + # Android application dependencies + if(${component} STREQUAL AndroidApplication) + find_package(EGL) + if(EGL_FOUND) + set(_MAGNUM_${_COMPONENT}_LIBRARIES android ${EGL_LIBRARY} ${_WINDOWCONTEXT_MAGNUM_LIBRARIES_DEPENDENCY}) + set(_MAGNUM_${_COMPONENT}_INCLUDE_DIRS ${ANDROID_NATIVE_APP_GLUE_INCLUDE_DIR}) + else() + unset(MAGNUM_${_COMPONENT}_LIBRARY) + endif() + endif() + # GLUT application dependencies if(${component} STREQUAL GlutApplication) find_package(GLUT) @@ -311,6 +405,9 @@ foreach(component ${Magnum_FIND_COMPONENTS}) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES Atlas.h) endif() + # The plugins don't have any dependencies, nothing additional to do for + # them + # Try to find the includes if(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES) find_path(_MAGNUM_${_COMPONENT}_INCLUDE_DIR @@ -326,7 +423,11 @@ foreach(component ${Magnum_FIND_COMPONENTS}) set(Magnum_${component}_FOUND TRUE) # Don't expose variables w/o dependencies to end users - mark_as_advanced(FORCE MAGNUM_${_COMPONENT}_LIBRARY _MAGNUM_${_COMPONENT}_INCLUDE_DIR) + mark_as_advanced(FORCE + MAGNUM_${_COMPONENT}_LIBRARY_DEBUG + MAGNUM_${_COMPONENT}_LIBRARY_RELEASE + MAGNUM_${_COMPONENT}_LIBRARY + _MAGNUM_${_COMPONENT}_INCLUDE_DIR) # Global aliases for Windowless*Application and *Application components. # If already set, unset them to avoid ambiguity. @@ -357,41 +458,42 @@ find_package_handle_standard_args(Magnum REQUIRED_VARS MAGNUM_LIBRARY MAGNUM_INCLUDE_DIR HANDLE_COMPONENTS) -# Dependent libraries and includes -set(MAGNUM_INCLUDE_DIRS ${MAGNUM_INCLUDE_DIR} - ${MAGNUM_INCLUDE_DIR}/MagnumExternal/OpenGL - ${CORRADE_INCLUDE_DIR}) -set(MAGNUM_LIBRARIES ${MAGNUM_LIBRARY} - ${CORRADE_UTILITY_LIBRARIES} - ${CORRADE_PLUGINMANAGER_LIBRARIES}) -if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES) - set(MAGNUM_LIBRARIES ${MAGNUM_LIBRARIES} ${OPENGL_gl_LIBRARY}) -else() - set(MAGNUM_LIBRARIES ${MAGNUM_LIBRARIES} ${OPENGLES2_LIBRARY}) -endif() - # Installation dirs include(CorradeLibSuffix) set(MAGNUM_LIBRARY_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) -set(MAGNUM_PLUGINS_INSTALL_DIR ${MAGNUM_LIBRARY_INSTALL_DIR}/magnum) -set(MAGNUM_PLUGINS_FONT_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/fonts) -set(MAGNUM_PLUGINS_FONTCONVERTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/fontconverters) -set(MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/imageconverters) -set(MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/importers) -set(MAGNUM_PLUGINS_AUDIOIMPORTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/audioimporters) +set(MAGNUM_PLUGINS_DEBUG_INSTALL_DIR ${MAGNUM_LIBRARY_INSTALL_DIR}/magnum-d) +set(MAGNUM_PLUGINS_RELEASE_INSTALL_DIR ${MAGNUM_LIBRARY_INSTALL_DIR}/magnum) +set(MAGNUM_PLUGINS_FONT_DEBUG_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_INSTALL_DIR}/fonts) +set(MAGNUM_PLUGINS_FONT_RELEASE_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_INSTALL_DIR}/fonts) +set(MAGNUM_PLUGINS_FONTCONVERTER_DEBUG_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_INSTALL_DIR}/fontconverters) +set(MAGNUM_PLUGINS_FONTCONVERTER_RELEASE_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_INSTALL_DIR}/fontconverters) +set(MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_INSTALL_DIR}/imageconverters) +set(MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_INSTALL_DIR}/imageconverters) +set(MAGNUM_PLUGINS_IMPORTER_DEBUG_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_INSTALL_DIR}/importers) +set(MAGNUM_PLUGINS_IMPORTER_RELEASE_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_INSTALL_DIR}/importers) +set(MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_INSTALL_DIR}/audioimporters) +set(MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_INSTALL_DIR}/audioimporters) set(MAGNUM_CMAKE_FIND_MODULE_INSTALL_DIR ${CMAKE_ROOT}/Modules) set(MAGNUM_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include/Magnum) set(MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include/MagnumPlugins) mark_as_advanced(FORCE + MAGNUM_LIBRARY_DEBUG + MAGNUM_LIBRARY_RELEASE MAGNUM_LIBRARY MAGNUM_INCLUDE_DIR MAGNUM_LIBRARY_INSTALL_DIR - MAGNUM_PLUGINS_INSTALL_DIR - MAGNUM_PLUGINS_FONT_INSTALL_DIR - MAGNUM_PLUGINS_FONTCONVERTER_INSTALL_DIR - MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR - MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR - MAGNUM_PLUGINS_AUDIOIMPORTER_INSTALL_DIR + MAGNUM_PLUGINS_DEBUG_INSTALL_DIR + MAGNUM_PLUGINS_RELEASE_INSTALL_DIR + MAGNUM_PLUGINS_FONT_DEBUG_INSTALL_DIR + MAGNUM_PLUGINS_FONT_RELEASE_INSTALL_DIR + MAGNUM_PLUGINS_FONTCONVERTER_DEBUG_INSTALL_DIR + MAGNUM_PLUGINS_FONTCONVERTER_RELEASE_INSTALL_DIR + MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_INSTALL_DIR + MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_INSTALL_DIR + MAGNUM_PLUGINS_IMPORTER_DEBUG_INSTALL_DIR + MAGNUM_PLUGINS_IMPORTER_RELEASE_INSTALL_DIR + MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_INSTALL_DIR + MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_INSTALL_DIR MAGNUM_CMAKE_MODULE_INSTALL_DIR MAGNUM_INCLUDE_INSTALL_DIR MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR) @@ -406,8 +508,7 @@ if(MAGNUM_BUILD_DEPRECATED) endif() # Get base plugin directory from main library location -get_filename_component(_MAGNUM_LIBRARY_PATH ${MAGNUM_LIBRARY} PATH) -set(MAGNUM_PLUGINS_DIR ${_MAGNUM_LIBRARY_PATH}/magnum +set(MAGNUM_PLUGINS_DIR ${_MAGNUM_LIBRARY_PATH}/magnum${_MAGNUM_PLUGINS_DIR_SUFFIX} CACHE PATH "Base directory where to look for Magnum plugins") # Plugin directories diff --git a/modules/FindOpenGLES3.cmake b/modules/FindOpenGLES3.cmake index 4fc74ecb2..fc9c9e573 100644 --- a/modules/FindOpenGLES3.cmake +++ b/modules/FindOpenGLES3.cmake @@ -34,7 +34,11 @@ # Library find_library(OPENGLES3_LIBRARY NAMES - GLESv2) # wtf? + GLESv3 + + # On some platforms (e.g. desktop emulation with Mesa or NVidia) ES3 + # support is provided in ES2 lib + GLESv2) # Include dir find_path(OPENGLES3_INCLUDE_DIR diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index c21cc7040..dc4913ad7 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -13,7 +13,7 @@ provides=('magnum-git') build() { mkdir -p "$startdir/build" - cd "$startdir/build/" + cd "$startdir/build" # Disable optimization (saves A LOT of compilation time) newcxxflags=$(echo $CXXFLAGS | sed s/-O.//g | sed s/-D_FORTIFY_SOURCE=.//g) @@ -29,6 +29,7 @@ build() { -DWITH_WINDOWLESSGLXAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ @@ -43,7 +44,7 @@ build() { check() { cd "$startdir/build" - ctest --output-on-failure + ctest --output-on-failure -j5 } package() { diff --git a/package/archlinux/PKGBUILD-android-arm b/package/archlinux/PKGBUILD-android-arm new file mode 100644 index 000000000..10e78f3a3 --- /dev/null +++ b/package/archlinux/PKGBUILD-android-arm @@ -0,0 +1,42 @@ +# Author: mosra +pkgname=android-arm-magnum +pkgver=dev +pkgrel=1 +pkgdesc="C++11 and OpenGL 2D/3D graphics engine (Android ARM)" +arch=('any') +url="http://mosra.cz/blog/magnum.php" +license=('MIT') +depends=('android-arm-corrade') +makedepends=('cmake' 'ninja' 'android-ndk') +options=('!strip' '!buildflags' 'staticlibs') + +build() { + if [ ! -d "$startdir/build-android-arm" ] ; then + mkdir "$startdir/build-android-arm" + cd "$startdir/build-android-arm" + + cmake .. \ + -DCMAKE_MODULE_PATH="$startdir/toolchains/modules" \ + -DCMAKE_TOOLCHAIN_FILE="$startdir/toolchains/generic/Android-ARM.cmake" \ + -DTARGET_GLES=ON \ + -DTARGET_GLES2=ON \ + -G Ninja + fi + + cd "$startdir/build-android-arm" + + cmake .. \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/opt/android-ndk/platforms/android-19/arch-arm/usr \ + -DWITH_MAGNUMFONT=ON \ + -DWITH_OBJIMPORTER=ON \ + -DWITH_TGAIMAGECONVERTER=ON \ + -DWITH_TGAIMPORTER=ON \ + -DWITH_ANDROIDAPPLICATION=ON + ninja +} + +package() { + cd "$startdir/build-android-arm" + DESTDIR="$pkgdir/" ninja install +} diff --git a/package/archlinux/PKGBUILD-android-x86 b/package/archlinux/PKGBUILD-android-x86 new file mode 100644 index 000000000..315471634 --- /dev/null +++ b/package/archlinux/PKGBUILD-android-x86 @@ -0,0 +1,42 @@ +# Author: mosra +pkgname=android-x86-magnum +pkgver=dev +pkgrel=1 +pkgdesc="C++11 and OpenGL 2D/3D graphics engine (Android x86)" +arch=('any') +url="http://mosra.cz/blog/magnum.php" +license=('MIT') +depends=('android-x86-corrade') +makedepends=('cmake' 'ninja' 'android-ndk') +options=('!strip' '!buildflags' 'staticlibs') + +build() { + if [ ! -d "$startdir/build-android-x86" ] ; then + mkdir "$startdir/build-android-x86" + cd "$startdir/build-android-x86" + + cmake .. \ + -DCMAKE_MODULE_PATH="$startdir/toolchains/modules" \ + -DCMAKE_TOOLCHAIN_FILE="$startdir/toolchains/generic/Android-x86.cmake" \ + -DTARGET_GLES=ON \ + -DTARGET_GLES2=ON \ + -G Ninja + fi + + cd "$startdir/build-android-x86" + + cmake .. \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/opt/android-ndk/platforms/android-19/arch-x86/usr \ + -DWITH_MAGNUMFONT=ON \ + -DWITH_OBJIMPORTER=ON \ + -DWITH_TGAIMAGECONVERTER=ON \ + -DWITH_TGAIMPORTER=ON \ + -DWITH_ANDROIDAPPLICATION=ON + ninja +} + +package() { + cd "$startdir/build-android-x86" + DESTDIR="$pkgdir/" ninja install +} diff --git a/package/archlinux/PKGBUILD-clang b/package/archlinux/PKGBUILD-clang index 45f89c36a..5e213067b 100644 --- a/package/archlinux/PKGBUILD-clang +++ b/package/archlinux/PKGBUILD-clang @@ -33,6 +33,7 @@ build() { -DWITH_WINDOWLESSGLXAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ @@ -46,7 +47,7 @@ build() { check() { cd "$startdir/build-clang" - ctest --output-on-failure + ctest --output-on-failure -j5 } package() { diff --git a/package/archlinux/PKGBUILD-clang-libc++ b/package/archlinux/PKGBUILD-clang-libc++ index 88bc44219..4ef88c32a 100644 --- a/package/archlinux/PKGBUILD-clang-libc++ +++ b/package/archlinux/PKGBUILD-clang-libc++ @@ -35,6 +35,7 @@ build() { -DWITH_WINDOWLESSGLXAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ @@ -48,7 +49,7 @@ build() { check() { cd "$startdir/build-clang-libc++" - ctest --output-on-failure + ctest --output-on-failure -j5 } package() { diff --git a/package/archlinux/PKGBUILD-emscripten b/package/archlinux/PKGBUILD-emscripten index 7de134779..e25fa841f 100644 --- a/package/archlinux/PKGBUILD-emscripten +++ b/package/archlinux/PKGBUILD-emscripten @@ -18,7 +18,6 @@ build() { cmake .. \ -DCMAKE_MODULE_PATH="$startdir/toolchains/modules" \ -DCMAKE_TOOLCHAIN_FILE="$startdir/toolchains/generic/Emscripten.cmake" \ - -DWITH_AUDIO=OFF \ -G Ninja fi @@ -27,9 +26,9 @@ build() { cmake .. \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr/emscripten/system \ - -DWITH_AUDIO=OFF \ -DWITH_SDL2APPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON diff --git a/package/archlinux/PKGBUILD-es2 b/package/archlinux/PKGBUILD-es2 index c1c9e82d3..3cbd211ff 100644 --- a/package/archlinux/PKGBUILD-es2 +++ b/package/archlinux/PKGBUILD-es2 @@ -23,6 +23,7 @@ build() { -DWITH_AUDIO=ON \ -DWITH_XEGLAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ @@ -33,7 +34,7 @@ build() { check() { cd "$startdir/build-es2" - ctest --output-on-failure + ctest --output-on-failure -j5 } package() { diff --git a/package/archlinux/PKGBUILD-es2desktop b/package/archlinux/PKGBUILD-es2desktop index 97e88d78f..dee3cf815 100644 --- a/package/archlinux/PKGBUILD-es2desktop +++ b/package/archlinux/PKGBUILD-es2desktop @@ -26,6 +26,7 @@ build() { -DWITH_GLXAPPLICATION=ON \ -DWITH_WINDOWLESSGLXAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ @@ -38,7 +39,7 @@ build() { check() { cd "$startdir/build-es2desktop" - ctest --output-on-failure + ctest --output-on-failure -j5 } package() { diff --git a/package/archlinux/PKGBUILD-es3 b/package/archlinux/PKGBUILD-es3 index dbecf3486..c13f5029f 100644 --- a/package/archlinux/PKGBUILD-es3 +++ b/package/archlinux/PKGBUILD-es3 @@ -23,6 +23,7 @@ build() { -DWITH_AUDIO=ON \ -DWITH_XEGLAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ @@ -33,7 +34,7 @@ build() { check() { cd "$startdir/build-es3" - ctest --output-on-failure + ctest --output-on-failure -j5 } package() { diff --git a/package/archlinux/PKGBUILD-es3desktop b/package/archlinux/PKGBUILD-es3desktop index a84fa45ea..2b7a1a517 100644 --- a/package/archlinux/PKGBUILD-es3desktop +++ b/package/archlinux/PKGBUILD-es3desktop @@ -26,6 +26,7 @@ build() { -DWITH_GLXAPPLICATION=ON \ -DWITH_WINDOWLESSGLXAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ @@ -38,7 +39,7 @@ build() { check() { cd "$startdir/build-es3desktop" - ctest --output-on-failure + ctest --output-on-failure -j5 } package() { diff --git a/package/archlinux/PKGBUILD-gcc46 b/package/archlinux/PKGBUILD-gcc46 index 15a1c6100..a3a545de7 100644 --- a/package/archlinux/PKGBUILD-gcc46 +++ b/package/archlinux/PKGBUILD-gcc46 @@ -33,6 +33,7 @@ build() { -DWITH_WINDOWLESSGLXAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ @@ -46,7 +47,7 @@ build() { check() { cd "$startdir/build-gcc46" - ctest --output-on-failure + ctest --output-on-failure -j5 } package() { diff --git a/package/archlinux/PKGBUILD-gcc47 b/package/archlinux/PKGBUILD-gcc47 index 600cc598f..eabf2142c 100644 --- a/package/archlinux/PKGBUILD-gcc47 +++ b/package/archlinux/PKGBUILD-gcc47 @@ -33,6 +33,7 @@ build() { -DWITH_WINDOWLESSGLXAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ @@ -46,7 +47,7 @@ build() { check() { cd "$startdir/build-gcc47" - ctest --output-on-failure + ctest --output-on-failure -j5 } package() { diff --git a/package/archlinux/PKGBUILD-gcc49 b/package/archlinux/PKGBUILD-gcc49 index 02d6db4da..1421f402c 100644 --- a/package/archlinux/PKGBUILD-gcc49 +++ b/package/archlinux/PKGBUILD-gcc49 @@ -33,6 +33,7 @@ build() { -DWITH_WINDOWLESSGLXAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ @@ -46,7 +47,7 @@ build() { check() { cd "$startdir/build-gcc49" - ctest --output-on-failure + ctest --output-on-failure -j5 } package() { diff --git a/package/archlinux/PKGBUILD-mingw32 b/package/archlinux/PKGBUILD-mingw32 index 594932067..a4b406ac8 100644 --- a/package/archlinux/PKGBUILD-mingw32 +++ b/package/archlinux/PKGBUILD-mingw32 @@ -12,7 +12,7 @@ options=('!buildflags' '!strip' 'staticlibs') build() { mkdir -p "$startdir/build-win" - cd "$startdir/build-win/" + cd "$startdir/build-win" unset LDFLAGS @@ -20,9 +20,11 @@ build() { -DCMAKE_TOOLCHAIN_FILE=../toolchains/archlinux/basic-mingw32.cmake \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr/i486-mingw32 \ + -DWITH_AUDIO=ON \ -DWITH_GLUTAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ diff --git a/package/archlinux/PKGBUILD-nacl-glibc b/package/archlinux/PKGBUILD-nacl-glibc index 3a3477d09..3798cde72 100644 --- a/package/archlinux/PKGBUILD-nacl-glibc +++ b/package/archlinux/PKGBUILD-nacl-glibc @@ -24,6 +24,7 @@ build() { -DWITH_NACLAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ @@ -44,6 +45,7 @@ build() { -DWITH_NACLAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ diff --git a/package/archlinux/PKGBUILD-nacl-newlib b/package/archlinux/PKGBUILD-nacl-newlib index d2a23fb6f..77e0fd084 100644 --- a/package/archlinux/PKGBUILD-nacl-newlib +++ b/package/archlinux/PKGBUILD-nacl-newlib @@ -20,12 +20,12 @@ build() { -DCMAKE_TOOLCHAIN_FILE="$startdir/toolchains/generic/NaCl-newlib-x86-32.cmake" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr/nacl \ - -DWITH_AUDIO=OFF \ -DWITH_MAGNUMINFO=ON \ -DWITH_NACLAPPLICATION=ON \ -DWITH_WINDOWLESSNACLAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ @@ -42,12 +42,12 @@ build() { -DCMAKE_TOOLCHAIN_FILE="$startdir/toolchains/generic/NaCl-newlib-x86-64.cmake" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr/nacl \ - -DWITH_AUDIO=OFF \ -DWITH_MAGNUMINFO=ON \ -DWITH_NACLAPPLICATION=ON \ -DWITH_WINDOWLESSNACLAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ diff --git a/package/archlinux/PKGBUILD-release b/package/archlinux/PKGBUILD-release index 61ff12d04..547900941 100644 --- a/package/archlinux/PKGBUILD-release +++ b/package/archlinux/PKGBUILD-release @@ -2,18 +2,43 @@ pkgname=magnum pkgver=dev.release pkgrel=1 -pkgdesc="C++11 and OpenGL 2D/3D graphics engine" +pkgdesc="C++11 and OpenGL 2D/3D graphics engine (debug+release libs)" arch=('i686' 'x86_64') url="http://mosra.cz/blog/magnum.php" license=('MIT') depends=('corrade' 'openal' 'sdl2' 'freeglut') makedepends=('cmake' 'ninja') +options=('!strip' 'staticlibs') provides=('magnum-git') -options=('staticlibs') build() { mkdir -p "$startdir/build" - cd "$startdir/build/" + cd "$startdir/build" + + cmake .. \ + -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DWITH_AUDIO=ON \ + -DWITH_GLUTAPPLICATION=ON \ + -DWITH_GLXAPPLICATION=ON \ + -DWITH_SDL2APPLICATION=ON \ + -DWITH_WINDOWLESSGLXAPPLICATION=ON \ + -DWITH_MAGNUMFONT=ON \ + -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ + -DWITH_TGAIMAGECONVERTER=ON \ + -DWITH_TGAIMPORTER=ON \ + -DWITH_WAVAUDIOIMPORTER=ON \ + -DWITH_DISTANCEFIELDCONVERTER=ON \ + -DWITH_FONTCONVERTER=ON \ + -DWITH_MAGNUMINFO=ON \ + -DBUILD_TESTS=ON \ + -DBUILD_GL_TESTS=ON \ + -G Ninja + ninja + + mkdir -p "$startdir/build-release" + cd "$startdir/build-release" cmake .. \ -DCMAKE_BUILD_TYPE=Release \ @@ -25,6 +50,7 @@ build() { -DWITH_WINDOWLESSGLXAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ @@ -39,10 +65,16 @@ build() { check() { cd "$startdir/build" - ctest --output-on-failure + ctest --output-on-failure -j5 + + cd "$startdir/build-release" + ctest --output-on-failure -j5 } package() { cd "$startdir/build" DESTDIR="$pkgdir/" ninja install + + cd "$startdir/build-release" + DESTDIR="$pkgdir/" ninja install/strip } diff --git a/package/archlinux/magnum-git/PKGBUILD b/package/archlinux/magnum-git/PKGBUILD index e3ca44e5f..660b77f98 100644 --- a/package/archlinux/magnum-git/PKGBUILD +++ b/package/archlinux/magnum-git/PKGBUILD @@ -45,6 +45,7 @@ build() { -DWITH_WINDOWLESSGLXAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ diff --git a/package/ci/jenkins-emscripten.xml b/package/ci/jenkins-emscripten.xml index bea34b38b..f0615f06f 100644 --- a/package/ci/jenkins-emscripten.xml +++ b/package/ci/jenkins-emscripten.xml @@ -73,6 +73,7 @@ cmake .. \ `#-DWITH_AUDIO=ON` \ -DWITH_SDL2APPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ `#-DWITH_WAVAUDIOIMPORTER=ON` \ diff --git a/package/ci/jenkins-gltests.xml b/package/ci/jenkins-gltests.xml index 8285cb565..df8dfa4d8 100644 --- a/package/ci/jenkins-gltests.xml +++ b/package/ci/jenkins-gltests.xml @@ -82,7 +82,7 @@ cd build-${compiler}-${libraries}-${compatibility}-${gl} ninja -ctest --output-on-failure -R GLTest || true +ctest --output-on-failure -R GLTest -j5 || true ]]> diff --git a/package/ci/jenkins-mingw32.xml b/package/ci/jenkins-mingw32.xml index 343df8bf8..9cf01d7de 100644 --- a/package/ci/jenkins-mingw32.xml +++ b/package/ci/jenkins-mingw32.xml @@ -87,6 +87,7 @@ cmake .. \ `#-DWITH_SDL2APPLICATION=ON` \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ @@ -96,7 +97,7 @@ cmake .. \ -G Ninja ninja -ninja install +ninja install/strip ]]> diff --git a/package/ci/jenkins-nacl.xml b/package/ci/jenkins-nacl.xml index 8ffc08905..8330d29e2 100644 --- a/package/ci/jenkins-nacl.xml +++ b/package/ci/jenkins-nacl.xml @@ -81,6 +81,7 @@ cmake .. \ -DWITH_NACLAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ `#-DWITH_WAVAUDIOIMPORTER=ON` \ @@ -88,7 +89,7 @@ cmake .. \ -G Ninja ninja -ninja install +ninja install/strip ]]> diff --git a/package/ci/jenkins.xml b/package/ci/jenkins.xml index 1984332d6..b8166fc20 100644 --- a/package/ci/jenkins.xml +++ b/package/ci/jenkins.xml @@ -99,22 +99,27 @@ fi if [ ${gl} = "desktop" ] ; then desktop_flag=ON + es_flag=OFF windowless_flag=ON elif [ ${gl} = "es2" ] ; then gl_flags="-DTARGET_GLES=ON -DTARGET_GLES2=ON" desktop_flag=OFF + es_flag=ON windowless_flag=OFF elif [ ${gl} = "es2desktop" ] ; then gl_flags="-DTARGET_GLES=ON -DTARGET_GLES2=ON -DTARGET_DESKTOP_GLES=ON" desktop_flag=OFF + es_flag=OFF windowless_flag=ON elif [ ${gl} = "es3" ] ; then gl_flags="-DTARGET_GLES=ON -DTARGET_GLES2=OFF" desktop_flag=OFF + es_flag=ON windowless_flag=OFF elif [ ${gl} = "es3desktop" ] ; then gl_flags="-DTARGET_GLES=ON -DTARGET_GLES2=OFF -DTARGET_DESKTOP_GLES=ON" desktop_flag=OFF + es_flag=OFF windowless_flag=ON fi @@ -137,8 +142,10 @@ cmake .. \ -DWITH_GLUTAPPLICATION=ON \ -DWITH_GLXAPPLICATION=ON \ -DWITH_SDL2APPLICATION=ON \ + -DWITH_XEGLAPPLICATION=${es_flag} \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=${desktop_flag} \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ @@ -148,8 +155,8 @@ cmake .. \ -G Ninja ninja -ctest --output-on-failure -E GLTest || true -ninja install +ctest --output-on-failure -E GLTest -j5 || true +ninja install/strip ]]> diff --git a/package/debian/rules b/package/debian/rules index 3723072df..16cda466a 100755 --- a/package/debian/rules +++ b/package/debian/rules @@ -11,6 +11,7 @@ override_dh_auto_configure: -DWITH_WINDOWLESSGLXAPPLICATION=ON \ -DWITH_MAGNUMFONT=ON \ -DWITH_MAGNUMFONTCONVERTER=ON \ + -DWITH_OBJIMPORTER=ON \ -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ diff --git a/src/Magnum/AbstractShaderProgram.cpp b/src/Magnum/AbstractShaderProgram.cpp index 890ef328e..8a24f51c3 100644 --- a/src/Magnum/AbstractShaderProgram.cpp +++ b/src/Magnum/AbstractShaderProgram.cpp @@ -271,40 +271,53 @@ void AbstractShaderProgram::bindFragmentDataLocationIndexed(UnsignedInt location } #endif -bool AbstractShaderProgram::link() { - /* Link shader program */ - glLinkProgram(_id); - - /* Check link status */ - GLint success, logLength; - glGetProgramiv(_id, GL_LINK_STATUS, &success); - glGetProgramiv(_id, GL_INFO_LOG_LENGTH, &logLength); - - /* Error or warning message. The string is returned null-terminated, scrap - the \0 at the end afterwards */ - std::string message(logLength, '\n'); - if(message.size() > 1) - glGetProgramInfoLog(_id, message.size(), nullptr, &message[0]); - message.resize(std::max(logLength, 1)-1); - - /* Show error log and delete shader */ - if(!success) { - Error out; - out.setFlag(Debug::NewLineAtTheEnd, false); - out.setFlag(Debug::SpaceAfterEachValue, false); - out << "AbstractShaderProgram: linking failed with the following message:\n" - << message; - - /* Or just warnings, if there are any */ - } else if(!message.empty()) { - Debug out; - out.setFlag(Debug::NewLineAtTheEnd, false); - out.setFlag(Debug::SpaceAfterEachValue, false); - out << "AbstractShaderProgram: linking succeeded with the following message:\n" - << message; +bool AbstractShaderProgram::link(std::initializer_list> shaders) { + bool allSuccess = true; + + /* Invoke (possibly parallel) linking on all shaders */ + for(AbstractShaderProgram& shader: shaders) glLinkProgram(shader._id); + + /* After linking phase, check status of all shaders */ + Int i = 1; + for(AbstractShaderProgram& shader: shaders) { + GLint success, logLength; + glGetProgramiv(shader._id, GL_LINK_STATUS, &success); + glGetProgramiv(shader._id, GL_INFO_LOG_LENGTH, &logLength); + + /* Error or warning message. The string is returned null-terminated, + scrap the \0 at the end afterwards */ + std::string message(logLength, '\n'); + if(message.size() > 1) + glGetProgramInfoLog(shader._id, message.size(), nullptr, &message[0]); + message.resize(std::max(logLength, 1)-1); + + /* Show error log */ + if(!success) { + Error out; + out.setFlag(Debug::NewLineAtTheEnd, false); + out.setFlag(Debug::SpaceAfterEachValue, false); + out << "AbstractShaderProgram::link(): linking"; + if(shaders.size() != 1) out << " of shader " << std::to_string(i); + out << " failed with the following message:\n" + << message; + + /* Or just warnings, if any */ + } else if(!message.empty()) { + Warning out; + out.setFlag(Debug::NewLineAtTheEnd, false); + out.setFlag(Debug::SpaceAfterEachValue, false); + out << "AbstractShaderProgram::link(): linking"; + if(shaders.size() != 1) out << " of shader " << std::to_string(i); + out << " succeeded with the following message:\n" + << message; + } + + /* Success of all depends on each of them */ + allSuccess = allSuccess && success; + ++i; } - return success; + return allSuccess; } Int AbstractShaderProgram::uniformLocation(const std::string& name) { diff --git a/src/Magnum/AbstractShaderProgram.h b/src/Magnum/AbstractShaderProgram.h index 1048c193f..ad58ad71b 100644 --- a/src/Magnum/AbstractShaderProgram.h +++ b/src/Magnum/AbstractShaderProgram.h @@ -29,6 +29,7 @@ * @brief Class @ref Magnum::AbstractShaderProgram */ +#include #include #include @@ -63,27 +64,21 @@ enum: UnsignedInt { NormalOutput = 1 }; @endcode -- **Uniform locations** for setting uniform data (see below) (private - variables), for example: -@code -Int TransformationUniform = 0, - ProjectionUniform = 1, - DiffuseTextureUniform = 2, - SpecularTextureUniform = 3; -@endcode -- **Constructor**, which attaches particular shaders, links the program and - gets uniform locations, for example: +- **Constructor**, which loads, compiles and attaches particular shaders and + links the program together, for example: @code MyShader() { - // Load shaders, compile them and attach them to the program + // Load shader sources Shader vert(Version::GL430, Shader::Type::Vertex); - vert.attachFile("PhongShader.vert"); - CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile()); - attachShader(vert); - Shader frag(Version::GL430, Shader::Type::Fragment); - frag.attachFile("PhongShader.vert"); - CORRADE_INTERNAL_ASSERT_OUTPUT(frag.compile()); + vert.addFile("PhongShader.vert"); + frag.addFile("PhongShader.vert"); + + // Invoke parallel compilation for best performance + CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag})); + + // Attach the shaders + attachShader(vert); attachShader(frag); // Link the program together @@ -94,24 +89,29 @@ MyShader() { protected @ref setUniform() functions. For usability purposes you can implement also method chaining. Example: @code -MyShader& setTransformation(const Matrix4& matrix) { - setUniform(TransformationUniform, matrix); +MyShader& setProjectionMatrix(const Matrix4& matrix) { + setUniform(0, matrix); + return *this; +} +MyShader& setTransformationMatrix(const Matrix4& matrix) { + setUniform(1, matrix); return *this; } -MyShader& setProjection(const Matrix4& matrix) { - setUniform(ProjectionUniform, matrix); +MyShader& setNormalMatrix(const Matrix3x3& matrix) { + setUniform(2, matrix); return *this; } @endcode -- **Texture setting functions** in which you bind the textures to particular - layers using @ref AbstractTexture::bind() and equivalent, for example: +- %Texture setting functions in which you bind the textures + to particular texture units using @ref Texture::bind() "*Texture::bind()" + and equivalents, for example: @code MyShader& setDiffuseTexture(Texture2D& texture) { - texture->bind(0); + texture.bind(0); return *this; } MyShader& setSpecularTexture(Texture2D& texture) { - texture->bind(1); + texture.bind(1); return *this; } @endcode @@ -141,7 +141,7 @@ If you don't have the required extension, declare the attributes without `layout()` qualifier and use functions @ref bindAttributeLocation() and @ref bindFragmentDataLocation() / @ref bindFragmentDataLocationIndexed() between attaching the shaders and linking the program. Note that additional syntax -changes may be needed for GLSL 1.20 and GLSL ES 1.0. +changes may be needed for GLSL 1.20 and GLSL ES. @code in vec4 position; in vec3 normal; @@ -191,21 +191,24 @@ code, e.g.: @code // GLSL 4.30, or #extension GL_ARB_explicit_uniform_location: enable -layout(location = 0) uniform mat4 transformation; -layout(location = 1) uniform mat4 projection; +layout(location = 0) uniform mat4 projectionMatrix; +layout(location = 1) uniform mat4 transformationMatrix; +layout(location = 2) uniform mat3 normalMatrix; @endcode If you don't have the required extension, declare the uniforms without the -`layout()` qualifier and get uniform location using @ref uniformLocation() -*after* linking stage. Note that additional syntax changes may be needed for -GLSL 1.20 and GLSL ES 1.0. +`layout()` qualifier, get uniform location using @ref uniformLocation() *after* +linking stage and then use the queried location in uniform setting functions. +Note that additional syntax changes may be needed for GLSL 1.20 and GLSL ES. @code -uniform mat4 transformation; -uniform mat4 projection; +uniform mat4 projectionMatrix; +uniform mat4 transformationMatrix; +uniform mat3 normalMatrix; @endcode @code -Int transformationUniform = uniformLocation("transformation"); -Int projectionUniform = uniformLocation("projection"); +Int projectionMatrixUniform = uniformLocation("projectionMatrix"); +Int transformationMatrixUniform = uniformLocation("transformationMatrix"); +Int normalMatrixUniform = uniformLocation("normalMatrix"); @endcode @see @ref maxUniformLocations() @@ -216,10 +219,10 @@ Int projectionUniform = uniformLocation("projection"); @ref Magnum::AbstractShaderProgram::uniformLocation() "uniformLocation()" instead. -@subsection AbstractShaderProgram-texture-layer Binding texture layer uniforms +@subsection AbstractShaderProgram-texture-units Specifying texture binding units -The preferred workflow is to specify texture layers directly in the shader -code, e.g.: +The preferred workflow is to specify texture binding unit directly in the +shader code, e.g.: @code // GLSL 4.20, or #extension GL_ARB_shading_language_420pack: enable @@ -227,25 +230,24 @@ layout(binding = 0) uniform sampler2D diffuseTexture; layout(binding = 1) uniform sampler2D specularTexture; @endcode -If you don't have the required extension (or if you want to change the layer -later), declare the uniforms without the `layout()` qualifier and set the -texture layer uniform using @ref setUniform(Int, const T&) "setUniform(Int, Int)". -Note that additional syntax changes may be needed for GLSL 1.20 and GLSL ES -1.0. +If you don't have the required extension, declare the uniforms without the +`layout()` qualifier and set the texture binding unit using +@ref setUniform(Int, const T&) "setUniform(Int, Int)". Note that additional +syntax changes may be needed for GLSL 1.20 and GLSL ES 1.0. @code uniform sampler2D diffuseTexture; uniform sampler2D specularTexture; @endcode @code -setUniform(DiffuseTextureUniform, 0); -setUniform(SpecularTextureUniform, 1); +setUniform(uniformLocation("diffuseTexture"), 0); +setUniform(uniformLocation("specularTexture"), 1); @endcode @see @ref Shader::maxTextureImageUnits() @requires_gl42 %Extension @extension{ARB,shading_language_420pack} for explicit - texture layer binding instead of using + texture binding unit instead of using @ref Magnum::AbstractShaderProgram::setUniform(Int, const T&) "setUniform(Int, Int)". -@requires_gl Explicit texture layer binding is not supported in OpenGL ES. Use +@requires_gl Explicit texture binding unit is not supported in OpenGL ES. Use @ref Magnum::AbstractShaderProgram::setUniform(Int, const T&) "setUniform(Int, Int)" instead. @@ -320,8 +322,6 @@ comes in handy. @see @ref portability-shaders @todo Use Containers::ArrayReference for setting uniform arrays? -@todo Compiling and linking more than one shader in parallel, then checking - status, should be faster -- https://twitter.com/g_truc/status/352778836657700866 @todo `GL_NUM_{PROGRAM,SHADER}_BINARY_FORMATS` + `GL_{PROGRAM,SHADER}_BINARY_FORMATS` (vector), (@extension{ARB,ES2_compatibility}) */ class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject { @@ -343,15 +343,6 @@ class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject { */ static Int maxVertexAttributes(); - #ifdef MAGNUM_BUILD_DEPRECATED - /** - * @copydoc maxVertexAttributes() - * @deprecated Use @ref Magnum::AbstractShaderProgram::maxVertexAttributes() "maxVertexAttributes()" - * instead. - */ - static CORRADE_DEPRECATED("use maxVertexAttributes() instead") Int maxSupportedVertexAttributeCount() { return maxVertexAttributes(); } - #endif - #ifndef MAGNUM_TARGET_GLES /** * @brief Max supported atomic counter buffer size @@ -562,6 +553,21 @@ class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject { #endif protected: + /** + * @brief Link the shader + * + * Returns `false` if linking of any shader failed, `true` if + * everything succeeded. Linker message (if any) is printed to error + * output. All attached shaders must be compiled with + * @ref Shader::compile() before linking. The operation is batched in a + * way that allows the driver to link multiple shaders simultaenously + * (i.e. in multiple threads). + * @see @fn_gl{LinkProgram}, @fn_gl{GetProgram} with + * @def_gl{LINK_STATUS} and @def_gl{INFO_LOG_LENGTH}, + * @fn_gl{GetProgramInfoLog} + */ + static bool link(std::initializer_list> shaders); + #ifndef MAGNUM_TARGET_GLES2 /** * @brief Allow retrieving program binary @@ -655,14 +661,12 @@ class MAGNUM_EXPORT AbstractShaderProgram: public AbstractObject { /** * @brief Link the shader * - * Returns `false` if linking failed, `true` otherwise. Compiler - * message (if any) is printed to error output. All attached shaders - * must be compiled with @ref Shader::compile() before linking. - * @see @fn_gl{LinkProgram}, @fn_gl{GetProgram} with - * @def_gl{LINK_STATUS} and @def_gl{INFO_LOG_LENGTH}, - * @fn_gl{GetProgramInfoLog} + * Links single shader. If possible, prefer to link multiple shaders + * at once using @ref link(std::initializer_list>) + * for improved performance, see its documentation for more + * information. */ - bool link(); + bool link() { return link({*this}); } /** * @brief Get uniform location @@ -963,9 +967,17 @@ template class AbstractShaderProgram::Attribute { * @brief Type * * Type used in shader code. - * @see @ref DataType + * @see @ref ScalarType, @ref DataType + */ + typedef T Type; + + /** + * @brief Scalar type + * + * The underlying scalar type of the attribute. + * @see @ref Type, @ref DataType */ - typedef typename Implementation::Attribute::Type Type; + typedef typename Implementation::Attribute::ScalarType ScalarType; /** * @brief Component count @@ -1257,7 +1269,7 @@ template struct Attribute; /* Base for float attributes */ struct FloatAttribute { - typedef Float Type; + typedef Float ScalarType; enum class DataType: GLenum { UnsignedByte = GL_UNSIGNED_BYTE, @@ -1300,7 +1312,7 @@ Debug MAGNUM_EXPORT operator<<(Debug debug, FloatAttribute::DataType value); #ifndef MAGNUM_TARGET_GLES2 /* Base for int attributes */ struct IntAttribute { - typedef Int Type; + typedef Int ScalarType; enum class DataType: GLenum { UnsignedByte = GL_UNSIGNED_BYTE, @@ -1327,7 +1339,7 @@ Debug MAGNUM_EXPORT operator<<(Debug debug, IntAttribute::DataType value); /* Base for unsigned int attributes */ struct UnsignedIntAttribute { - typedef UnsignedInt Type; + typedef UnsignedInt ScalarType; typedef IntAttribute::DataType DataType; #if !defined(CORRADE_GCC45_COMPATIBILITY) && !defined(CORRADE_MSVC2013_COMPATIBILITY) @@ -1349,7 +1361,7 @@ struct UnsignedIntAttribute { #ifndef MAGNUM_TARGET_GLES /* Base for double attributes */ struct DoubleAttribute { - typedef Double Type; + typedef Double ScalarType; enum class DataType: GLenum { Double = GL_DOUBLE @@ -1372,7 +1384,7 @@ Debug MAGNUM_EXPORT operator<<(Debug debug, DoubleAttribute::DataType value); /* Floating-point four-component vector is absolutely special case */ template<> struct Attribute> { - typedef Float Type; + typedef Float ScalarType; enum class Components: GLint { One = 1, diff --git a/src/Magnum/AbstractTexture.cpp b/src/Magnum/AbstractTexture.cpp index 0aa3e9ad7..c9374ee5b 100644 --- a/src/Magnum/AbstractTexture.cpp +++ b/src/Magnum/AbstractTexture.cpp @@ -48,7 +48,6 @@ namespace Magnum { #ifdef MAGNUM_BUILD_DEPRECATED Int AbstractTexture::maxLayers() { return Shader::maxCombinedTextureImageUnits(); } -Int AbstractTexture::maxSupportedLayerCount() { return Shader::maxCombinedTextureImageUnits(); } #endif #ifndef MAGNUM_TARGET_GLES @@ -92,6 +91,72 @@ Int AbstractTexture::maxIntegerSamples() { } #endif +void AbstractTexture::unbind(const Int textureUnit) { + Implementation::TextureState* const textureState = Context::current()->state().texture; + + /* If given texture unit is already unbound, nothing to do */ + if(textureState->bindings[textureUnit].second == 0) return; + + /* Unbind the texture, reset state tracker */ + Context::current()->state().texture->unbindImplementation(textureUnit); + textureState->bindings[textureUnit] = {}; +} + +void AbstractTexture::unbindImplementationDefault(const GLint textureUnit) { + Implementation::TextureState* const textureState = Context::current()->state().texture; + + /* Activate given texture unit if not already active, update state tracker */ + if(textureState->currentTextureUnit != textureUnit) + glActiveTexture(GL_TEXTURE0 + (textureState->currentTextureUnit = textureUnit)); + + CORRADE_INTERNAL_ASSERT(textureState->bindings[textureUnit].first != 0); + glBindTexture(textureState->bindings[textureUnit].first, 0); +} + +#ifndef MAGNUM_TARGET_GLES +void AbstractTexture::unbindImplementationMulti(const GLint textureUnit) { + constexpr static GLuint zero = 0; + glBindTextures(textureUnit, 1, &zero); +} + +void AbstractTexture::unbindImplementationDSA(const GLint textureUnit) { + Implementation::TextureState* const textureState = Context::current()->state().texture; + + CORRADE_INTERNAL_ASSERT(textureState->bindings[textureUnit].first != 0); + glBindMultiTextureEXT(GL_TEXTURE0 + textureUnit, textureState->bindings[textureUnit].first, 0); +} +#endif + +void AbstractTexture::bind(const Int firstTextureUnit, std::initializer_list textures) { + /* State tracker is updated in the implementations */ + Context::current()->state().texture->bindMultiImplementation(firstTextureUnit, textures); +} + +void AbstractTexture::bindImplementationFallback(const GLint firstTextureUnit, std::initializer_list textures) { + Int unit = firstTextureUnit; + for(AbstractTexture* const texture: textures) { + if(texture) texture->bind(unit); + else unbind(unit); + ++unit; + } +} + +#ifndef MAGNUM_TARGET_GLES +void AbstractTexture::bindImplementationMulti(const GLint firstTextureUnit, std::initializer_list textures) { + Implementation::TextureState* const textureState = Context::current()->state().texture; + + /* Create array of IDs and also update bindings in state tracker */ + Containers::Array ids{textures.size()}; + Int i{}; + for(const AbstractTexture* const texture: textures) { + textureState->bindings[firstTextureUnit + i].second = ids[i] = texture ? texture->id() : 0; + ++i; + } + + glBindTextures(firstTextureUnit, ids.size(), ids); +} +#endif + AbstractTexture::AbstractTexture(GLenum target): _target(target) { glGenTextures(1, &_id); } @@ -101,9 +166,9 @@ AbstractTexture::~AbstractTexture() { if(!_id) return; /* Remove all bindings */ - std::vector& bindings = Context::current()->state().texture->bindings; + std::vector>& bindings = Context::current()->state().texture->bindings; for(auto it = bindings.begin(); it != bindings.end(); ++it) - if(*it == _id) *it = 0; + if(it->second == _id) *it = {}; glDeleteTextures(1, &_id); } @@ -117,32 +182,53 @@ AbstractTexture& AbstractTexture::setLabel(const std::string& label) { return *this; } -void AbstractTexture::bind(Int layer) { +void AbstractTexture::bind(Int textureUnit) { Implementation::TextureState* const textureState = Context::current()->state().texture; - /* If already bound in given layer, nothing to do */ - if(textureState->bindings[layer] == _id) return; + /* If already bound in given texture unit, nothing to do */ + if(textureState->bindings[textureUnit].second == _id) return; - (this->*Context::current()->state().texture->bindImplementation)(layer); + /* Update state tracker, bind the texture to the unit */ + textureState->bindings[textureUnit] = {_target, _id}; + (this->*Context::current()->state().texture->bindImplementation)(textureUnit); } -void AbstractTexture::bindImplementationDefault(GLint layer) { +void AbstractTexture::bindImplementationDefault(GLint textureUnit) { Implementation::TextureState* const textureState = Context::current()->state().texture; - /* Change to given layer, if not already there */ - if(textureState->currentLayer != layer) - glActiveTexture(GL_TEXTURE0 + (textureState->currentLayer = layer)); + /* Activate given texture unit if not already active, update state tracker */ + if(textureState->currentTextureUnit != textureUnit) + glActiveTexture(GL_TEXTURE0 + (textureState->currentTextureUnit = textureUnit)); - /* Bind the texture to the layer */ - glBindTexture(_target, (textureState->bindings[layer] = _id)); + glBindTexture(_target, _id); } #ifndef MAGNUM_TARGET_GLES -void AbstractTexture::bindImplementationDSA(GLint layer) { - glBindMultiTextureEXT(GL_TEXTURE0 + layer, _target, (Context::current()->state().texture->bindings[layer] = _id)); +void AbstractTexture::bindImplementationMulti(GLint textureUnit) { + glBindTextures(textureUnit, 1, &_id); +} + +void AbstractTexture::bindImplementationDSA(GLint textureUnit) { + glBindMultiTextureEXT(GL_TEXTURE0 + textureUnit, _target, _id); +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +void AbstractTexture::setBaseLevel(Int level) { + (this->*Context::current()->state().texture->parameteriImplementation)(GL_TEXTURE_BASE_LEVEL, level); } #endif +void AbstractTexture::setMaxLevel(Int level) { + (this->*Context::current()->state().texture->parameteriImplementation)( + #ifndef MAGNUM_TARGET_GLES2 + GL_TEXTURE_MAX_LEVEL + #else + GL_TEXTURE_MAX_LEVEL_APPLE + #endif + , level); +} + void AbstractTexture::setMinificationFilter(Sampler::Filter filter, Sampler::Mipmap mipmap) { (this->*Context::current()->state().texture->parameteriImplementation)(GL_TEXTURE_MIN_FILTER, GLint(filter)|GLint(mipmap)); } @@ -159,6 +245,16 @@ void AbstractTexture::setBorderColor(const Color4& color) { #endif } +#ifndef MAGNUM_TARGET_GLES +void AbstractTexture::setBorderColor(const Vector4ui& color) { + (this->*Context::current()->state().texture->parameterIuivImplementation)(GL_TEXTURE_BORDER_COLOR, color.data()); +} + +void AbstractTexture::setBorderColor(const Vector4i& color) { + (this->*Context::current()->state().texture->parameterIivImplementation)(GL_TEXTURE_BORDER_COLOR, color.data()); +} +#endif + void AbstractTexture::setMaxAnisotropy(const Float anisotropy) { (this->*Context::current()->state().texture->setMaxAnisotropyImplementation)(anisotropy); } @@ -183,21 +279,26 @@ void AbstractTexture::mipmapImplementationDSA() { #endif void AbstractTexture::bindInternal() { + /* Using glBindTextures() here is meaningless, because the non-DSA + functions need to have the texture bound in *currently active* unit, + so we would need to call glActiveTexture() afterwards anyway. */ + Implementation::TextureState* const textureState = Context::current()->state().texture; - /* If the texture is already bound in current layer, nothing to do */ - if(textureState->bindings[textureState->currentLayer] == _id) + /* If the texture is already bound in current unit, nothing to do */ + if(textureState->bindings[textureState->currentTextureUnit].second == _id) return; - /* Set internal layer as active if not already */ - CORRADE_INTERNAL_ASSERT(textureState->maxLayers > 1); - const GLint internalLayer = textureState->maxLayers-1; - if(textureState->currentLayer != internalLayer) - glActiveTexture(GL_TEXTURE0 + (textureState->currentLayer = internalLayer)); + /* Set internal unit as active if not already, update state tracker */ + CORRADE_INTERNAL_ASSERT(textureState->maxTextureUnits > 1); + const GLint internalTextureUnit = textureState->maxTextureUnits-1; + if(textureState->currentTextureUnit != internalTextureUnit) + glActiveTexture(GL_TEXTURE0 + (textureState->currentTextureUnit = internalTextureUnit)); - /* Bind the texture to internal layer, if not already */ - if(textureState->bindings[internalLayer] != _id) - glBindTexture(_target, (textureState->bindings[internalLayer] = _id)); + /* Bind the texture to internal unit if not already, update state tracker */ + if(textureState->bindings[internalTextureUnit].second == _id) return; + textureState->bindings[internalTextureUnit] = {_target, _id}; + glBindTexture(_target, _id); } ColorFormat AbstractTexture::imageFormatForInternalFormat(const TextureFormat internalFormat) { @@ -602,6 +703,26 @@ void AbstractTexture::parameterImplementationDSA(GLenum parameter, const GLfloat } #endif +#ifndef MAGNUM_TARGET_GLES +void AbstractTexture::parameterImplementationDefault(GLenum parameter, const GLuint* values) { + bindInternal(); + glTexParameterIuiv(_target, parameter, values); +} + +void AbstractTexture::parameterImplementationDSA(GLenum parameter, const GLuint* values) { + glTextureParameterIuivEXT(_id, _target, parameter, values); +} + +void AbstractTexture::parameterImplementationDefault(GLenum parameter, const GLint* values) { + bindInternal(); + glTexParameterIiv(_target, parameter, values); +} + +void AbstractTexture::parameterImplementationDSA(GLenum parameter, const GLint* values) { + glTextureParameterIivEXT(_id, _target, parameter, values); +} +#endif + void AbstractTexture::setMaxAnisotropyImplementationNoOp(GLfloat) {} void AbstractTexture::setMaxAnisotropyImplementationExt(GLfloat anisotropy) { @@ -768,6 +889,36 @@ void AbstractTexture::storageImplementationDSA(GLenum target, GLsizei levels, Te } #endif +#ifndef MAGNUM_TARGET_GLES +void AbstractTexture::storageMultisampleImplementationFallback(const GLenum target, const GLsizei samples, const TextureFormat internalFormat, const Vector2i& size, const GLboolean fixedSampleLocations) { + bindInternal(); + glTexImage2DMultisample(target, samples, GLenum(internalFormat), size.x(), size.y(), fixedSampleLocations); +} + +void AbstractTexture::storageMultisampleImplementationDefault(const GLenum target, const GLsizei samples, const TextureFormat internalFormat, const Vector2i& size, const GLboolean fixedSampleLocations) { + bindInternal(); + glTexStorage2DMultisample(target, samples, GLenum(internalFormat), size.x(), size.y(), fixedSampleLocations); +} + +void AbstractTexture::storageMultisampleImplementationDSA(const GLenum target, const GLsizei samples, const TextureFormat internalFormat, const Vector2i& size, const GLboolean fixedSampleLocations) { + glTextureStorage2DMultisampleEXT(_id, target, samples, GLenum(internalFormat), size.x(), size.y(), fixedSampleLocations); +} + +void AbstractTexture::storageMultisampleImplementationFallback(const GLenum target, const GLsizei samples, const TextureFormat internalFormat, const Vector3i& size, const GLboolean fixedSampleLocations) { + bindInternal(); + glTexImage3DMultisample(target, samples, GLenum(internalFormat), size.x(), size.y(), size.z(), fixedSampleLocations); +} + +void AbstractTexture::storageMultisampleImplementationDefault(const GLenum target, const GLsizei samples, const TextureFormat internalFormat, const Vector3i& size, const GLboolean fixedSampleLocations) { + bindInternal(); + glTexStorage3DMultisample(target, samples, GLenum(internalFormat), size.x(), size.y(), size.z(), fixedSampleLocations); +} + +void AbstractTexture::storageMultisampleImplementationDSA(const GLenum target, const GLsizei samples, const TextureFormat internalFormat, const Vector3i& size, const GLboolean fixedSampleLocations) { + glTextureStorage3DMultisampleEXT(_id, target, samples, GLenum(internalFormat), size.x(), size.y(), size.z(), fixedSampleLocations); +} +#endif + #ifndef MAGNUM_TARGET_GLES void AbstractTexture::getImageImplementationDefault(const GLenum target, const GLint level, const ColorFormat format, const ColorType type, const std::size_t, GLvoid* const data) { bindInternal(); @@ -975,6 +1126,16 @@ void AbstractTexture::DataHelper<3>::setStorage(AbstractTexture& texture, const (texture.*Context::current()->state().texture->storage3DImplementation)(target, levels, internalFormat, size); } +#ifndef MAGNUM_TARGET_GLES +void AbstractTexture::DataHelper<2>::setStorageMultisample(AbstractTexture& texture, const GLenum target, const GLsizei samples, const TextureFormat internalFormat, const Vector2i& size, const GLboolean fixedSampleLocations) { + (texture.*Context::current()->state().texture->storage2DMultisampleImplementation)(target, samples, internalFormat, size, fixedSampleLocations); +} + +void AbstractTexture::DataHelper<3>::setStorageMultisample(AbstractTexture& texture, const GLenum target, const GLsizei samples, const TextureFormat internalFormat, const Vector3i& size, const GLboolean fixedSampleLocations) { + (texture.*Context::current()->state().texture->storage3DMultisampleImplementation)(target, samples, internalFormat, size, fixedSampleLocations); +} +#endif + #ifndef MAGNUM_TARGET_GLES void AbstractTexture::DataHelper<1>::setImage(AbstractTexture& texture, const GLenum target, const GLint level, const TextureFormat internalFormat, const ImageReference1D& image) { Buffer::unbind(Buffer::Target::PixelUnpack); diff --git a/src/Magnum/AbstractTexture.h b/src/Magnum/AbstractTexture.h index 0ef26095c..f0a9fead8 100644 --- a/src/Magnum/AbstractTexture.h +++ b/src/Magnum/AbstractTexture.h @@ -49,20 +49,31 @@ Encapsulates one OpenGL texture object. See @ref Texture, @ref CubeMapTexture and @ref CubeMapTextureArray documentation for more information and usage examples. +@section AbstractTexture-webgl-restrictions WebGL restrictions + +@ref MAGNUM_TARGET_WEBGL "WebGL" puts some restrictions on type of data +submitted to @ref Texture::setSubImage() "*Texture::setSubImage()", see its +documentation for details. + @section AbstractTexture-performance-optimization Performance optimizations and security -The engine tracks currently bound textures in all available layers to avoid -unnecessary calls to @fn_gl{ActiveTexture} and @fn_gl{BindTexture}. %Texture -configuration functions use dedicated highest available texture layer to not -affect active bindings in user layers. %Texture limits and +The engine tracks currently bound textures in all available texture units to +avoid unnecessary calls to @fn_gl{ActiveTexture} and @fn_gl{BindTexture}. +%Texture configuration functions use dedicated highest available texture unit +to not affect active bindings in user units. %Texture limits and implementation-defined values (such as @ref maxColorSamples()) are cached, so repeated queries don't result in repeated @fn_gl{Get} calls. -If extension @extension{EXT,direct_state_access} is available, @ref bind() uses -DSA function to avoid unnecessary calls to @fn_gl{ActiveTexture}. Also all -texture configuration and data updating functions use DSA functions to avoid -unnecessary calls to @fn_gl{ActiveTexture} and @fn_gl{BindTexture}. See -respective function documentation for more information. +If extension @extension{ARB,multi_bind} is available, @ref bind() uses +@fn_gl{BindTextures} to avoid unnecessary calls to @fn_gl{ActiveTexture}. +Otherwise, if extension @extension{EXT,direct_state_access} is available, +@ref bind() uses @fn_gl_extension{BindMultiTexture,EXT,direct_state_access} +function. + +In addition, if extension @extension{EXT,direct_state_access} is available, +also all texture configuration and data updating functions use DSA functions +to avoid unnecessary calls to @fn_gl{ActiveTexture} and @fn_gl{BindTexture}. +See respective function documentation for more information. If extension @extension{ARB,robustness} is available, image reading operations (such as @ref Texture::image()) are protected from buffer overflow. However, if @@ -72,7 +83,7 @@ there isn't any function combining both features. To achieve least state changes, fully configure each texture in one run -- method chaining comes in handy -- and try to have often used textures in -dedicated layers, not occupied by other textures. First configure the texture +dedicated units, not occupied by other textures. First configure the texture and *then* set the data, so OpenGL can optimize them to match the settings. To avoid redundant consistency checks and memory reallocations when updating texture data, set texture storage at once using @ref Texture::setStorage() "setStorage()" @@ -85,7 +96,7 @@ OpenGL ES 3.0 or @es_extension{EXT,texture_storage} in OpenGL ES 2.0 is not available, the feature is emulated with sequence of @ref Texture::setImage() "setImage()" calls. -You can use functions @ref Texture::invalidateImage() and +You can use functions @ref Texture::invalidateImage() "invalidateImage()" and @ref Texture::invalidateSubImage() "invalidateSubImage()" if you don't need texture data anymore to avoid unnecessary memory operations performed by OpenGL in order to preserve the data. If running on OpenGL ES or extension @@ -103,6 +114,10 @@ functions do nothing. @todo `GL_NUM_COMPRESSED_TEXTURE_FORMATS` when compressed textures are implemented @todo `GL_MAX_SAMPLE_MASK_WORDS` when @extension{ARB,texture_multisample} is done @todo Query for immutable levels (@extension{ARB,ES3_compatibility}) +@bug If using @extension{ARB,multi_bind} and the texture is bound right after + construction, the @fn_gl{BindTextures} call will fail with + Renderer::Error::InvalidOperation, because the texture doesn't yet have + associated target */ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { friend struct Implementation::TextureState; @@ -117,15 +132,6 @@ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { static CORRADE_DEPRECATED("use Shader::maxCombinedTextureImageUnits() instead") Int maxLayers(); #endif - #ifdef MAGNUM_BUILD_DEPRECATED - /** - * @copybrief Shader::maxCombinedTextureImageUnits() - * @deprecated Use @ref Magnum::Shader::maxCombinedTextureImageUnits() "Shader::maxCombinedTextureImageUnits()" - * instead. - */ - static CORRADE_DEPRECATED("use Shader::maxCombinedTextureImageUnits() instead") Int maxSupportedLayerCount(); - #endif - #ifndef MAGNUM_TARGET_GLES /** * @brief Max supported color sample count @@ -162,12 +168,32 @@ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { #endif /** - * @brief Destructor + * @brief Unbind any texture from given texture unit * - * Deletes associated OpenGL texture. - * @see @fn_gl{DeleteTextures} + * If @extension{ARB,multi_bind} (part of OpenGL 4.4) or + * @extension{EXT,direct_state_access} is not available, the texture + * unit is made active before binding the texture. + * @note This function is meant to be used only internally from + * @ref AbstractShaderProgram subclasses. See its documentation + * for more information. + * @see @ref bind(), @ref Shader::maxCombinedTextureImageUnits(), + * @fn_gl{ActiveTexture}, @fn_gl{BindTexture}, @fn_gl{BindTextures} + * or @fn_gl_extension{BindMultiTexture,EXT,direct_state_access} */ - ~AbstractTexture(); + static void unbind(Int textureUnit); + + /** + * @brief Bind textures to given range of texture units + * + * Binds first texture in the list to @p firstTextureUnit, second to + * `firstTextureUnit + 1` etc. If any texture is `nullptr`, given + * texture unit is unbound. If @extension{ARB,multi_bind} (part of + * OpenGL 4.4) is not available, the feature is emulated with sequence + * of @ref bind(Int) / @ref unbind() calls. + * @see @fn_gl{BindTextures}, eventually @fn_gl{ActiveTexture}, + * @fn_gl{BindTexture} or @fn_gl_extension{BindMultiTexture,EXT,direct_state_access} + */ + static void bind(Int firstTextureUnit, std::initializer_list textures); /** @brief Copying is not allowed */ AbstractTexture(const AbstractTexture&) = delete; @@ -175,6 +201,14 @@ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { /** @brief Move constructor */ AbstractTexture(AbstractTexture&& other) noexcept; + /** + * @brief Destructor + * + * Deletes associated OpenGL texture. + * @see @fn_gl{DeleteTextures} + */ + ~AbstractTexture(); + /** @brief Copying is not allowed */ AbstractTexture& operator=(const AbstractTexture&) = delete; @@ -211,19 +245,20 @@ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { GLuint id() const { return _id; } /** - * @brief Bind texture for rendering + * @brief Bind texture to given texture unit * - * Sets current texture as active in given layer. Note that only one - * texture can be bound to given layer. If @extension{EXT,direct_state_access} - * is not available, the layer is made active before binding the - * texture. + * If @extension{ARB,multi_bind} (part of OpenGL 4.4) or + * @extension{EXT,direct_state_access} is not available, the texture + * unit is made active before binding the texture. * @note This function is meant to be used only internally from * @ref AbstractShaderProgram subclasses. See its documentation * for more information. - * @see @ref maxLayers(), @fn_gl{ActiveTexture}, @fn_gl{BindTexture} or - * @fn_gl_extension{BindMultiTexture,EXT,direct_state_access} + * @see @ref bind(Int, std::initializer_list), + * @ref unbind(), @ref Shader::maxCombinedTextureImageUnits(), + * @fn_gl{ActiveTexture}, @fn_gl{BindTexture}, @fn_gl{BindTextures} + * or @fn_gl_extension{BindMultiTexture,EXT,direct_state_access} */ - void bind(Int layer); + void bind(Int textureUnit); #ifdef DOXYGEN_GENERATING_OUTPUT private: @@ -234,12 +269,18 @@ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { explicit AbstractTexture(GLenum target); - /* Unlike bind() this also sets the binding layer as active */ + /* Unlike bind() this also sets the texture binding unit as active */ void MAGNUM_LOCAL bindInternal(); + #ifndef MAGNUM_TARGET_GLES2 + void setBaseLevel(Int level); + #endif + void setMaxLevel(Int level); void setMinificationFilter(Sampler::Filter filter, Sampler::Mipmap mipmap); void setMagnificationFilter(Sampler::Filter filter); void setBorderColor(const Color4& color); + void setBorderColor(const Vector4i& color); + void setBorderColor(const Vector4ui& color); void setMaxAnisotropy(Float anisotropy); void invalidateImage(Int level); void generateMipmap(); @@ -252,24 +293,36 @@ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { GLenum _target; private: - void MAGNUM_LOCAL bindImplementationDefault(GLint layer); + static void MAGNUM_LOCAL unbindImplementationDefault(GLint textureUnit); #ifndef MAGNUM_TARGET_GLES - void MAGNUM_LOCAL bindImplementationDSA(GLint layer); + static void MAGNUM_LOCAL unbindImplementationMulti(GLint textureUnit); + static void MAGNUM_LOCAL unbindImplementationDSA(GLint textureUnit); #endif - void MAGNUM_LOCAL parameterImplementationDefault(GLenum parameter, GLint value); + static void MAGNUM_LOCAL bindImplementationFallback(GLint firstTextureUnit, std::initializer_list textures); #ifndef MAGNUM_TARGET_GLES - void MAGNUM_LOCAL parameterImplementationDSA(GLenum parameter, GLint value); + static void MAGNUM_LOCAL bindImplementationMulti(GLint firstTextureUnit, std::initializer_list textures); #endif - void MAGNUM_LOCAL parameterImplementationDefault(GLenum parameter, GLfloat value); + void MAGNUM_LOCAL bindImplementationDefault(GLint textureUnit); #ifndef MAGNUM_TARGET_GLES - void MAGNUM_LOCAL parameterImplementationDSA(GLenum parameter, GLfloat value); + void MAGNUM_LOCAL bindImplementationMulti(GLint textureUnit); + void MAGNUM_LOCAL bindImplementationDSA(GLint textureUnit); #endif + void MAGNUM_LOCAL parameterImplementationDefault(GLenum parameter, GLint value); + void MAGNUM_LOCAL parameterImplementationDefault(GLenum parameter, GLfloat value); void MAGNUM_LOCAL parameterImplementationDefault(GLenum parameter, const GLfloat* values); #ifndef MAGNUM_TARGET_GLES + void MAGNUM_LOCAL parameterImplementationDefault(GLenum parameter, const GLuint* values); + void MAGNUM_LOCAL parameterImplementationDefault(GLenum parameter, const GLint* values); + #endif + #ifndef MAGNUM_TARGET_GLES + void MAGNUM_LOCAL parameterImplementationDSA(GLenum parameter, GLint value); + void MAGNUM_LOCAL parameterImplementationDSA(GLenum parameter, GLfloat value); void MAGNUM_LOCAL parameterImplementationDSA(GLenum parameter, const GLfloat* values); + void MAGNUM_LOCAL parameterImplementationDSA(GLenum parameter, const GLuint* values); + void MAGNUM_LOCAL parameterImplementationDSA(GLenum parameter, const GLint* values); #endif void MAGNUM_LOCAL setMaxAnisotropyImplementationNoOp(GLfloat); @@ -303,6 +356,16 @@ class MAGNUM_EXPORT AbstractTexture: public AbstractObject { void MAGNUM_LOCAL storageImplementationDSA(GLenum target, GLsizei levels, TextureFormat internalFormat, const Vector3i& size); #endif + #ifndef MAGNUM_TARGET_GLES + void MAGNUM_LOCAL storageMultisampleImplementationFallback(GLenum target, GLsizei samples, TextureFormat internalFormat, const Vector2i& size, GLboolean fixedsamplelocations); + void MAGNUM_LOCAL storageMultisampleImplementationDefault(GLenum target, GLsizei samples, TextureFormat internalFormat, const Vector2i& size, GLboolean fixedsamplelocations); + void MAGNUM_LOCAL storageMultisampleImplementationDSA(GLenum target, GLsizei samples, TextureFormat internalFormat, const Vector2i& size, GLboolean fixedsamplelocations); + + void MAGNUM_LOCAL storageMultisampleImplementationFallback(GLenum target, GLsizei samples, TextureFormat internalFormat, const Vector3i& size, GLboolean fixedsamplelocations); + void MAGNUM_LOCAL storageMultisampleImplementationDefault(GLenum target, GLsizei samples, TextureFormat internalFormat, const Vector3i& size, GLboolean fixedsamplelocations); + void MAGNUM_LOCAL storageMultisampleImplementationDSA(GLenum target, GLsizei samples, TextureFormat internalFormat, const Vector3i& size, GLboolean fixedsamplelocations); + #endif + #ifndef MAGNUM_TARGET_GLES void MAGNUM_LOCAL getImageImplementationDefault(GLenum target, GLint level, ColorFormat format, ColorType type, std::size_t dataSize, GLvoid* data); void MAGNUM_LOCAL getImageImplementationDSA(GLenum target, GLint level, ColorFormat format, ColorType type, std::size_t dataSize, GLvoid* data); @@ -399,6 +462,8 @@ template<> struct MAGNUM_EXPORT AbstractTexture::DataHelper<2> { static void setStorage(AbstractTexture& texture, GLenum target, GLsizei levels, TextureFormat internalFormat, const Vector2i& size); + static void setStorageMultisample(AbstractTexture& texture, GLenum target, GLsizei samples, TextureFormat internalFormat, const Vector2i& size, GLboolean fixedSampleLocations); + static void setImage(AbstractTexture& texture, GLenum target, GLint level, TextureFormat internalFormat, const ImageReference2D& image); #ifndef MAGNUM_TARGET_GLES2 static void setImage(AbstractTexture& texture, GLenum target, GLint level, TextureFormat internalFormat, BufferImage2D& image); @@ -434,6 +499,8 @@ template<> struct MAGNUM_EXPORT AbstractTexture::DataHelper<3> { static void setStorage(AbstractTexture& texture, GLenum target, GLsizei levels, TextureFormat internalFormat, const Vector3i& size); + static void setStorageMultisample(AbstractTexture& texture, GLenum target, GLsizei samples, TextureFormat internalFormat, const Vector3i& size, GLboolean fixedSampleLocations); + static void setImage(AbstractTexture& texture, GLenum target, GLint level, TextureFormat internalFormat, const ImageReference3D& image); #ifndef MAGNUM_TARGET_GLES2 static void setImage(AbstractTexture& texture, GLenum target, GLint level, TextureFormat internalFormat, BufferImage3D& image); diff --git a/src/Magnum/Audio/CMakeLists.txt b/src/Magnum/Audio/CMakeLists.txt index f4f13603e..f5b35054d 100644 --- a/src/Magnum/Audio/CMakeLists.txt +++ b/src/Magnum/Audio/CMakeLists.txt @@ -46,6 +46,7 @@ set(MagnumAudio_HEADERS visibility.h) add_library(MagnumAudio ${SHARED_OR_STATIC} ${MagnumAudio_SOURCES}) +set_target_properties(MagnumAudio PROPERTIES DEBUG_POSTFIX "-d") target_link_libraries(MagnumAudio ${CORRADE_PLUGINMANAGER_LIBRARIES} ${OPENAL_LIBRARY}) install(TARGETS MagnumAudio diff --git a/src/Magnum/Buffer.h b/src/Magnum/Buffer.h index 167795702..70f37b18c 100644 --- a/src/Magnum/Buffer.h +++ b/src/Magnum/Buffer.h @@ -166,6 +166,23 @@ for(std::size_t i: {7, 27, 56, 128}) { CORRADE_INTERNAL_ASSERT_OUTPUT(buffer.unmap()); @endcode +@section Buffer-webgl-restrictions WebGL and NaCl restrictions + +Buffers in @ref MAGNUM_TARGET_WEBGL "WebGL" and @ref CORRADE_TARGET_NACL "NaCl" +need to be bound only to one unique target, i.e., @ref Buffer bound to +@ref Buffer::Target::Array cannot be later rebound to +@ref Buffer::Target::ElementArray. However, %Magnum by default uses any +sufficient target when binding the buffer internally (e.g. for setting data). +To avoid GL errors, set target hint to desired target, either in constructor or +using @ref Buffer::setTargetHint(): +@code +Buffer vertices{Buffer::Target::Array}; +Buffer indices{Buffer::Target::ElementArray}; +@endcode + +To ease up the development, @ref Mesh checks proper target hint when adding +vertex and index buffers in both WebGL and NaCl. + @section Buffer-performance-optimization Performance optimizations The engine tracks currently bound buffers to avoid unnecessary calls to @@ -661,17 +678,6 @@ class MAGNUM_EXPORT Buffer: public AbstractObject { */ Buffer& setData(Containers::ArrayReference data, BufferUsage usage); - #ifdef MAGNUM_BUILD_DEPRECATED - /** - * @copybrief setData(Containers::ArrayReference, BufferUsage) - * @deprecated Use @ref Magnum::Buffer::setData(Containers::ArrayReference, BufferUsage) "setData(Containers::ArrayReference, BufferUsage)" - * instead. - */ - CORRADE_DEPRECATED("use setData(Containers::ArrayReference, BufferUsage) instead") Buffer& setData(GLsizeiptr size, const GLvoid* data, BufferUsage usage) { - return setData({data, std::size_t(size)}, usage); - } - #endif - /** @overload */ template Buffer& setData(const std::vector& data, BufferUsage usage) { setData({data.data(), data.size()}, usage); @@ -698,17 +704,6 @@ class MAGNUM_EXPORT Buffer: public AbstractObject { */ Buffer& setSubData(GLintptr offset, Containers::ArrayReference data); - #ifdef MAGNUM_BUILD_DEPRECATED - /** - * @copybrief setSubData(GLintptr, Containers::ArrayReference) - * @deprecated Use @ref Magnum::Buffer::setSubData(GLintptr, Containers::ArrayReference) "setSubData(GLintptr, Containers::ArrayReference)" - * instead. - */ - CORRADE_DEPRECATED("use setSubData(GLintptr, Containers::ArrayReference) instead") Buffer& setSubData(GLintptr offset, GLsizeiptr size, const GLvoid* data) { - return setSubData(offset, {data, std::size_t(size)}); - } - #endif - /** @overload */ template Buffer& setSubData(GLintptr offset, const std::vector& data) { setSubData(offset, {data.data(), data.size()}); diff --git a/src/Magnum/CMakeLists.txt b/src/Magnum/CMakeLists.txt index 304705c65..5ff0d2030 100644 --- a/src/Magnum/CMakeLists.txt +++ b/src/Magnum/CMakeLists.txt @@ -130,9 +130,7 @@ set(Magnum_HEADERS # Deprecated headers if(BUILD_DEPRECATED) set(Magnum_HEADERS ${Magnum_HEADERS} - DebugMarker.h - ImageFormat.h - Swizzle.h) + DebugMarker.h) endif() # Desktop-only headers and libraries @@ -162,6 +160,7 @@ add_library(MagnumMathObjects OBJECT ${MagnumMath_SRCS}) add_library(Magnum ${SHARED_OR_STATIC} ${Magnum_SRCS} $) +set_target_properties(Magnum PROPERTIES DEBUG_POSTFIX "-d") # TODO: fix when CMake sets target_EXPORTS for OBJECT targets as well if(NOT BUILD_STATIC OR BUILD_STATIC_PIC) @@ -237,7 +236,9 @@ if(BUILD_TESTS) # Libraries with graceful assert for testing add_library(MagnumMathTestLib ${SHARED_OR_STATIC} $) - set_target_properties(MagnumMathTestLib PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) + set_target_properties(MagnumMathTestLib PROPERTIES + COMPILE_FLAGS "-DCORRADE_GRACEFUL_ASSERT" + DEBUG_POSTFIX "-d") target_link_libraries(MagnumMathTestLib ${CORRADE_UTILITY_LIBRARY}) # On Windows we need to install first and then run the tests to avoid "DLL diff --git a/src/Magnum/Context.cpp b/src/Magnum/Context.cpp index 697d8ffec..94b12904d 100644 --- a/src/Magnum/Context.cpp +++ b/src/Magnum/Context.cpp @@ -61,6 +61,7 @@ const std::vector& Extension::extensions(Version version) { _extension(GL,EXT,texture_filter_anisotropic), _extension(GL,EXT,texture_mirror_clamp), _extension(GL,EXT,direct_state_access), + _extension(GL,EXT,shader_integer_mix), _extension(GL,EXT,debug_label), _extension(GL,EXT,debug_marker), _extension(GL,GREMEDY,string_marker)}; @@ -213,6 +214,7 @@ const std::vector& Extension::extensions(Version version) { _extension(GL,ANGLE,framebuffer_multisample), _extension(GL,ANGLE,depth_texture), _extension(GL,APPLE,framebuffer_multisample), + _extension(GL,APPLE,texture_max_level), _extension(GL,ARM,rgba8), _extension(GL,EXT,texture_type_2_10_10_10_REV), _extension(GL,EXT,discard_framebuffer), @@ -283,7 +285,7 @@ Context::Context() { everything possible. */ if(ogl_LoadFunctions() == ogl_LOAD_FAILED) { Error() << "ExtensionWrangler: cannot initialize glLoadGen"; - std::exit(1); + std::exit(64); } #endif @@ -313,7 +315,7 @@ Context::Context() { const std::string version = versionString(); #ifndef MAGNUM_TARGET_GLES if(version.compare(0, 4, "2.1 ") == 0) - #elif defined(CORRADE_TARGET_EMSCRIPTEN) + #elif defined(MAGNUM_TARGET_WEBGL) if(version.find("WebGL 1") != std::string::npos) #else if(version.find("OpenGL ES 2.0") != std::string::npos) @@ -327,7 +329,7 @@ Context::Context() { #endif } else { Error() << "Context: unsupported version string:" << version; - std::exit(1); + std::exit(65); } } #endif @@ -346,17 +348,17 @@ Context::Context() { #ifndef MAGNUM_TARGET_GLES if(!isVersionSupported(Version::GL210)) #elif defined(MAGNUM_TARGET_GLES2) - if(!isVersionSupported(Version::GLES200)) + if(_version != Version::GLES200) #else - if(!isVersionSupported(Version::GLES300)) + if(_version != Version::GLES300) #endif { #ifndef MAGNUM_TARGET_GLES - Error() << "Context: unsupported OpenGL version" << Int(_version); + Error() << "Context: unsupported OpenGL version" << Magnum::version(_version); #else - Error() << "Context: unsupported OpenGL ES version" << Int(_version); + Error() << "Context: unsupported OpenGL ES version" << Magnum::version(_version); #endif - std::exit(1); + std::exit(66); } /* Context flags are supported since GL 3.0 */ @@ -411,46 +413,15 @@ Context::Context() { #endif } - /* Check for presence of extensions in future versions */ - #ifndef MAGNUM_TARGET_GLES2 - GLint extensionCount = 0; - glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount); - #ifndef MAGNUM_TARGET_GLES3 - if(extensionCount || isVersionSupported(Version::GL300)) - #endif - { - _supportedExtensions.reserve(extensionCount); - for(GLint i = 0; i != extensionCount; ++i) { - const std::string extension(reinterpret_cast(glGetStringi(GL_EXTENSIONS, i))); - auto found = futureExtensions.find(extension); - if(found != futureExtensions.end()) { - _supportedExtensions.push_back(found->second); - extensionStatus.set(found->second._index); - } + /* Check for presence of future and vendor extensions */ + const std::vector extensions = extensionStrings(); + for(const std::string& extension: extensions) { + const auto found = futureExtensions.find(extension); + if(found != futureExtensions.end()) { + _supportedExtensions.push_back(found->second); + extensionStatus.set(found->second._index); } } - #ifndef MAGNUM_TARGET_GLES3 - else - #endif - #endif - - #ifndef MAGNUM_TARGET_GLES3 - /* OpenGL 2.1 / OpenGL ES 2.0 doesn't have glGetStringi() */ - { - /* Don't crash when glGetString() returns nullptr */ - const char* e = reinterpret_cast(glGetString(GL_EXTENSIONS)); - if(e) { - std::vector extensions = Utility::String::split(e, ' '); - for(auto it = extensions.begin(); it != extensions.end(); ++it) { - auto found = futureExtensions.find(*it); - if(found != futureExtensions.end()) { - _supportedExtensions.push_back(found->second); - extensionStatus.set(found->second._index); - } - } - } - } - #endif /* Reset minimal required version to Version::None for whole array */ for(auto it = _extensionRequiredVersion.begin(); it != _extensionRequiredVersion.end(); ++it) @@ -504,6 +475,38 @@ std::vector Context::shadingLanguageVersionStrings() const { #endif } +std::vector Context::extensionStrings() const { + std::vector extensions; + + #ifndef MAGNUM_TARGET_GLES2 + GLint extensionCount = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount); + #ifndef MAGNUM_TARGET_GLES3 + if(extensionCount || isVersionSupported(Version::GL300)) + #endif + { + extensions.reserve(extensionCount); + for(GLint i = 0; i != extensionCount; ++i) + extensions.push_back(reinterpret_cast(glGetStringi(GL_EXTENSIONS, i))); + } + #ifndef MAGNUM_TARGET_GLES3 + else + #endif + #endif + + #ifndef MAGNUM_TARGET_GLES3 + /* OpenGL 2.1 / OpenGL ES 2.0 doesn't have glGetStringi() */ + { + /* Don't crash when glGetString() returns nullptr (i.e. don't trust the + old implementations) */ + const char* e = reinterpret_cast(glGetString(GL_EXTENSIONS)); + if(e) extensions = Utility::String::splitWithoutEmptyParts(e, ' '); + } + #endif + + return extensions; +} + Version Context::supportedVersion(std::initializer_list versions) const { for(auto it = versions.begin(); it != versions.end(); ++it) if(isVersionSupported(*it)) return *it; @@ -515,4 +518,19 @@ Version Context::supportedVersion(std::initializer_list versions) const #endif } +#ifndef DOXYGEN_GENERATING_OUTPUT +Debug operator<<(Debug debug, const Context::Flag value) { + switch(value) { + #define _c(value) case Context::Flag::value: return debug << "Context::Flag::" #value; + _c(Debug) + #ifndef MAGNUM_TARGET_GLES + _c(RobustAccess) + #endif + #undef _c + } + + return debug << "Context::Flag::(invalid)"; +} +#endif + } diff --git a/src/Magnum/Context.h b/src/Magnum/Context.h index 088f585d0..3e291d9d0 100644 --- a/src/Magnum/Context.h +++ b/src/Magnum/Context.h @@ -107,7 +107,7 @@ class MAGNUM_EXPORT Context { /** * @brief Context flag * - * @see @ref Flags, @ref flags() + * @see @ref Flags, @ref flags(), @ref Platform::Sdl2Application::Configuration::setFlags() "Platform::*Application::Configuration::setFlags()" */ enum class Flag: GLint { /** @@ -123,14 +123,22 @@ class MAGNUM_EXPORT Context { #ifndef MAGNUM_TARGET_GLES /** - * Context with robust buffer access + * Context with robust access * @requires_extension %Extension @extension{ARB,robustness} * @requires_es_extension %Extension @es_extension{EXT,robustness} * @todo In ES available under glGetIntegerv(CONTEXT_ROBUST_ACCESS_EXT), * how to make it compatible? */ + RobustAccess = GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB, + + #ifdef MAGNUM_BUILD_DEPRECATED + /** + * @copybrief Context::Flag::RobustAccess + * @deprecated Use @ref Magnum::Context::Flag::RobustAccess "Context::Flag::RobustAccess" instead. + */ Robustness = GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB #endif + #endif }; /** @@ -237,6 +245,19 @@ class MAGNUM_EXPORT Context { */ std::vector shadingLanguageVersionStrings() const; + /** + * @brief Extension strings + * + * The result is *not* cached, repeated queries will result in repeated + * OpenGL calls. Note that this function returns list of all extensions + * reported by the driver (even those not supported by %Magnum), see + * @ref supportedExtensions(), @ref Extension::extensions() or + * @ref isExtensionSupported() for alternatives. + * @see @fn_gl{Get} with @def_gl{NUM_EXTENSIONS}, @fn_gl{GetString} + * with @def_gl{EXTENSIONS} + */ + std::vector extensionStrings() const; + /** @brief Context flags */ Flags flags() const { return _flags; } @@ -386,6 +407,9 @@ class MAGNUM_EXPORT Context { Implementation::State* _state; }; +/** @debugoperator{Magnum::Context} */ +MAGNUM_EXPORT Debug operator<<(Debug debug, Context::Flag value); + /** @hideinitializer @brief Assert that given OpenGL version is supported @param version Version diff --git a/src/Magnum/CubeMapTexture.h b/src/Magnum/CubeMapTexture.h index bdd3a19a4..6aada4175 100644 --- a/src/Magnum/CubeMapTexture.h +++ b/src/Magnum/CubeMapTexture.h @@ -108,6 +108,20 @@ class CubeMapTexture: public AbstractTexture { } #endif + #ifndef MAGNUM_TARGET_GLES2 + /** @copydoc Texture::setBaseLevel() */ + CubeMapTexture& setBaseLevel(Int level) { + AbstractTexture::setBaseLevel(level); + return *this; + } + #endif + + /** @copydoc Texture::setMaxLevel() */ + CubeMapTexture& setMaxLevel(Int level) { + AbstractTexture::setMaxLevel(level); + return *this; + } + /** @copydoc Texture::setMinificationFilter() */ CubeMapTexture& setMinificationFilter(Sampler::Filter filter, Sampler::Mipmap mipmap = Sampler::Mipmap::Base) { AbstractTexture::setMinificationFilter(filter, mipmap); @@ -126,12 +140,26 @@ class CubeMapTexture: public AbstractTexture { return *this; } - /** @copydoc Texture::setBorderColor() */ + /** @copydoc Texture::setBorderColor(const Color4&) */ CubeMapTexture& setBorderColor(const Color4& color) { AbstractTexture::setBorderColor(color); return *this; } + #ifndef MAGNUM_TARGET_GLES + /** @copydoc Texture::setBorderColor(const Vector4ui&) */ + CubeMapTexture& setBorderColor(const Vector4ui& color) { + AbstractTexture::setBorderColor(color); + return *this; + } + + /** @copydoc Texture::setBorderColor(const Vector4i&) */ + CubeMapTexture& setBorderColor(const Vector4i& color) { + AbstractTexture::setBorderColor(color); + return *this; + } + #endif + /** @copydoc Texture::setMaxAnisotropy() */ CubeMapTexture& setMaxAnisotropy(Float anisotropy) { AbstractTexture::setMaxAnisotropy(anisotropy); diff --git a/src/Magnum/CubeMapTextureArray.h b/src/Magnum/CubeMapTextureArray.h index a29d6ed96..5c01802e5 100644 --- a/src/Magnum/CubeMapTextureArray.h +++ b/src/Magnum/CubeMapTextureArray.h @@ -99,6 +99,18 @@ class CubeMapTextureArray: public AbstractTexture { } #endif + /** @copydoc Texture::setBaseLevel() */ + CubeMapTextureArray& setBaseLevel(Int level) { + AbstractTexture::setBaseLevel(level); + return *this; + } + + /** @copydoc Texture::setMaxLevel() */ + CubeMapTextureArray& setMaxLevel(Int level) { + AbstractTexture::setMaxLevel(level); + return *this; + } + /** @copydoc Texture::setMinificationFilter() */ CubeMapTextureArray& setMinificationFilter(Sampler::Filter filter, Sampler::Mipmap mipmap = Sampler::Mipmap::Base) { AbstractTexture::setMinificationFilter(filter, mipmap); @@ -123,6 +135,18 @@ class CubeMapTextureArray: public AbstractTexture { return *this; } + /** @copydoc Texture::setBorderColor(const Vector4ui&) */ + CubeMapTextureArray& setBorderColor(const Vector4ui& color) { + AbstractTexture::setBorderColor(color); + return *this; + } + + /** @copydoc Texture::setBorderColor(const Vector4i&) */ + CubeMapTextureArray& setBorderColor(const Vector4i& color) { + AbstractTexture::setBorderColor(color); + return *this; + } + /** @copydoc Texture::setMaxAnisotropy() */ CubeMapTextureArray& setMaxAnisotropy(Float anisotropy) { AbstractTexture::setMaxAnisotropy(anisotropy); @@ -150,14 +174,12 @@ class CubeMapTextureArray: public AbstractTexture { return *this; } - #ifndef MAGNUM_TARGET_GLES /** * @brief Read given mip level of texture to image * @param level Mip level * @param image %Image where to put the data * * See @ref Texture::image(Int, Image&) for more information. - * @requires_gl %Texture image queries are not available in OpenGL ES. */ void image(Int level, Image3D& image) { AbstractTexture::image<3>(GL_TEXTURE_CUBE_MAP_ARRAY, level, image); @@ -171,12 +193,10 @@ class CubeMapTextureArray: public AbstractTexture { * * See @ref Texture::image(Int, BufferImage&, BufferUsage) for more * information. - * @requires_gl %Texture image queries are not available in OpenGL ES. */ void image(Int level, BufferImage3D& image, BufferUsage usage) { AbstractTexture::image<3>(GL_TEXTURE_CUBE_MAP_ARRAY, level, image, usage); } - #endif /** * @brief Set image data diff --git a/src/Magnum/DebugTools/CMakeLists.txt b/src/Magnum/DebugTools/CMakeLists.txt index 619c34fb1..4455b248e 100644 --- a/src/Magnum/DebugTools/CMakeLists.txt +++ b/src/Magnum/DebugTools/CMakeLists.txt @@ -51,6 +51,7 @@ set(MagnumDebugTools_HEADERS visibility.h) add_library(MagnumDebugTools ${SHARED_OR_STATIC} ${MagnumDebugTools_SRCS}) +set_target_properties(MagnumDebugTools PROPERTIES DEBUG_POSTFIX "-d") if(BUILD_STATIC_PIC) # TODO: CMake 2.8.9 has this as POSITION_INDEPENDENT_CODE property set_target_properties(MagnumDebugTools PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") diff --git a/src/Magnum/Extensions.h b/src/Magnum/Extensions.h index d9271cffc..e2f07f16d 100644 --- a/src/Magnum/Extensions.h +++ b/src/Magnum/Extensions.h @@ -182,6 +182,7 @@ namespace GL { _extension(GL,EXT,transform_feedback, GL210, GL300) // #352 _extension(GL,EXT,direct_state_access, GL210, None) // #353 _extension(GL,EXT,texture_snorm, GL300, GL310) // #365 + _extension(GL,EXT,shader_integer_mix, GL300, None) // #437 _extension(GL,EXT,debug_label, GL210, None) // #439 _extension(GL,EXT,debug_marker, GL210, None) // #440 } namespace GREMEDY { @@ -211,6 +212,9 @@ namespace GL { _extension(GL,APPLE,framebuffer_multisample, GLES200, GLES300) // #78 #endif _extension(GL,APPLE,texture_format_BGRA8888, GLES200, None) // #79 + #ifdef MAGNUM_TARGET_GLES2 + _extension(GL,APPLE,texture_max_level, GLES200, None) // #80 + #endif } namespace ARM { #ifdef MAGNUM_TARGET_GLES2 _extension(GL,ARM,rgba8, GLES200, GLES300) // #82 @@ -245,6 +249,9 @@ namespace GL { _extension(GL,EXT,map_buffer_range, GLES200, GLES300) // #121 #endif _extension(GL,EXT,disjoint_timer_query, GLES200, None) // #150 + #ifndef MAGNUM_TARGET_GLES2 + _extension(GL,EXT,shader_integer_mix, GLES300, None) // #161 + #endif } namespace KHR { _extension(GL,KHR,debug, GLES200, None) // #118 } namespace NV { diff --git a/src/Magnum/Image.h b/src/Magnum/Image.h index baac399ed..5cd691983 100644 --- a/src/Magnum/Image.h +++ b/src/Magnum/Image.h @@ -138,8 +138,8 @@ template class Image: public AbstractImage { /** * @brief Release data storage * - * Returns the data pointer and resets internal state to default. - * Deleting the returned array is user responsibility. + * Releases the ownership of the data pointer and resets internal state + * to default. Deleting the returned array is then user responsibility. * @see @ref setData() */ unsigned char* release(); diff --git a/src/Magnum/Implementation/TextureState.cpp b/src/Magnum/Implementation/TextureState.cpp index dc1eab00b..60d362f43 100644 --- a/src/Magnum/Implementation/TextureState.cpp +++ b/src/Magnum/Implementation/TextureState.cpp @@ -36,20 +36,45 @@ namespace Magnum { namespace Implementation { -TextureState::TextureState(Context& context, std::vector& extensions): maxLayers(0), maxMaxAnisotropy(0.0f), currentLayer(0) +TextureState::TextureState(Context& context, std::vector& extensions): maxTextureUnits(0), maxMaxAnisotropy(0.0f), currentTextureUnit(0) #ifndef MAGNUM_TARGET_GLES , maxColorSamples(0), maxDepthSamples(0), maxIntegerSamples(0), bufferOffsetAlignment(0) #endif { + /* Bind implementation */ + #ifndef MAGNUM_TARGET_GLES + if(context.isExtensionSupported()) { + extensions.push_back(Extensions::GL::ARB::multi_bind::string()); + + unbindImplementation = &AbstractTexture::unbindImplementationMulti; + bindMultiImplementation = &AbstractTexture::bindImplementationMulti; + bindImplementation = &AbstractTexture::bindImplementationMulti; + + } else if(context.isExtensionSupported()) { + /* Extension name added below */ + + unbindImplementation = &AbstractTexture::unbindImplementationDSA; + bindMultiImplementation = &AbstractTexture::bindImplementationFallback; + bindImplementation = &AbstractTexture::bindImplementationDSA; + + } else + #endif + { + unbindImplementation = &AbstractTexture::unbindImplementationDefault; + bindMultiImplementation = &AbstractTexture::bindImplementationFallback; + bindImplementation = &AbstractTexture::bindImplementationDefault; + } + /* DSA/non-DSA implementation */ #ifndef MAGNUM_TARGET_GLES if(context.isExtensionSupported()) { extensions.push_back(Extensions::GL::EXT::direct_state_access::string()); - bindImplementation = &AbstractTexture::bindImplementationDSA; parameteriImplementation = &AbstractTexture::parameterImplementationDSA; parameterfImplementation = &AbstractTexture::parameterImplementationDSA; parameterfvImplementation = &AbstractTexture::parameterImplementationDSA; + parameterIuivImplementation = &AbstractTexture::parameterImplementationDSA; + parameterIivImplementation = &AbstractTexture::parameterImplementationDSA; getLevelParameterivImplementation = &AbstractTexture::getLevelParameterImplementationDSA; mipmapImplementation = &AbstractTexture::mipmapImplementationDSA; getImageImplementation = &AbstractTexture::getImageImplementationDSA; @@ -65,11 +90,12 @@ TextureState::TextureState(Context& context, std::vector& extension } else #endif { - bindImplementation = &AbstractTexture::bindImplementationDefault; parameteriImplementation = &AbstractTexture::parameterImplementationDefault; parameterfImplementation = &AbstractTexture::parameterImplementationDefault; parameterfvImplementation = &AbstractTexture::parameterImplementationDefault; #ifndef MAGNUM_TARGET_GLES + parameterIuivImplementation = &AbstractTexture::parameterImplementationDefault; + parameterIivImplementation = &AbstractTexture::parameterImplementationDefault; getLevelParameterivImplementation = &AbstractTexture::getLevelParameterImplementationDefault; #endif mipmapImplementation = &AbstractTexture::mipmapImplementationDefault; @@ -153,6 +179,25 @@ TextureState::TextureState(Context& context, std::vector& extension } #endif + #ifndef MAGNUM_TARGET_GLES + /* Storage implementation for multisample textures. The fallback doesn't + have DSA alternative, so it must be handled specially. */ + if(context.isExtensionSupported()) { + extensions.push_back(Extensions::GL::ARB::texture_storage_multisample::string()); + + if(context.isExtensionSupported()) { + storage2DMultisampleImplementation = &AbstractTexture::storageMultisampleImplementationDSA; + storage3DMultisampleImplementation = &AbstractTexture::storageMultisampleImplementationDSA; + } else { + storage2DMultisampleImplementation = &AbstractTexture::storageMultisampleImplementationDefault; + storage3DMultisampleImplementation = &AbstractTexture::storageMultisampleImplementationDefault; + } + } else { + storage2DMultisampleImplementation = &AbstractTexture::storageMultisampleImplementationFallback; + storage3DMultisampleImplementation = &AbstractTexture::storageMultisampleImplementationFallback; + } + #endif + /* Anisotropic filter implementation */ if(context.isExtensionSupported()) { extensions.push_back(Extensions::GL::EXT::texture_filter_anisotropic::string()); @@ -160,10 +205,10 @@ TextureState::TextureState(Context& context, std::vector& extension setMaxAnisotropyImplementation = &AbstractTexture::setMaxAnisotropyImplementationExt; } else setMaxAnisotropyImplementation = &AbstractTexture::setMaxAnisotropyImplementationNoOp; - /* Resize bindings array to hold all possible layers */ - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxLayers); - CORRADE_INTERNAL_ASSERT(maxLayers > 0); - bindings.resize(maxLayers); + /* Resize bindings array to hold all possible texture units */ + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); + CORRADE_INTERNAL_ASSERT(maxTextureUnits > 0); + bindings.resize(maxTextureUnits); } TextureState::~TextureState() = default; diff --git a/src/Magnum/Implementation/TextureState.h b/src/Magnum/Implementation/TextureState.h index 8b0c79628..160769c43 100644 --- a/src/Magnum/Implementation/TextureState.h +++ b/src/Magnum/Implementation/TextureState.h @@ -55,10 +55,14 @@ struct TextureState { explicit TextureState(Context& context, std::vector& extensions); ~TextureState(); + void(*unbindImplementation)(GLint); + void(*bindMultiImplementation)(GLint, std::initializer_list); void(AbstractTexture::*bindImplementation)(GLint); void(AbstractTexture::*parameteriImplementation)(GLenum, GLint); void(AbstractTexture::*parameterfImplementation)(GLenum, GLfloat); void(AbstractTexture::*parameterfvImplementation)(GLenum, const GLfloat*); + void(AbstractTexture::*parameterIuivImplementation)(GLenum, const GLuint*); + void(AbstractTexture::*parameterIivImplementation)(GLenum, const GLint*); void(AbstractTexture::*setMaxAnisotropyImplementation)(GLfloat); void(AbstractTexture::*getLevelParameterivImplementation)(GLenum, GLint, GLenum, GLint*); void(AbstractTexture::*mipmapImplementation)(); @@ -68,6 +72,8 @@ struct TextureState { void(AbstractTexture::*storage2DImplementation)(GLenum, GLsizei, TextureFormat, const Vector2i&); void(AbstractTexture::*storage3DImplementation)(GLenum, GLsizei, TextureFormat, const Vector3i&); #ifndef MAGNUM_TARGET_GLES + void(AbstractTexture::*storage2DMultisampleImplementation)(GLenum, GLsizei, TextureFormat, const Vector2i&, GLboolean); + void(AbstractTexture::*storage3DMultisampleImplementation)(GLenum, GLsizei, TextureFormat, const Vector3i&, GLboolean); void(AbstractTexture::*getImageImplementation)(GLenum, GLint, ColorFormat, ColorType, std::size_t, GLvoid*); void(AbstractTexture::*image1DImplementation)(GLenum, GLint, TextureFormat, const Math::Vector<1, GLsizei>&, ColorFormat, ColorType, const GLvoid*); #endif @@ -86,9 +92,9 @@ struct TextureState { void(BufferTexture::*setBufferRangeImplementation)(BufferTextureFormat, Buffer&, GLintptr, GLsizeiptr); #endif - GLint maxLayers; + GLint maxTextureUnits; GLfloat maxMaxAnisotropy; - GLint currentLayer; + GLint currentTextureUnit; #ifndef MAGNUM_TARGET_GLES GLint maxColorSamples, maxDepthSamples, @@ -96,7 +102,7 @@ struct TextureState { bufferOffsetAlignment; #endif - std::vector bindings; + std::vector> bindings; }; }} diff --git a/src/Magnum/Implementation/setupDriverWorkarounds.cpp b/src/Magnum/Implementation/setupDriverWorkarounds.cpp index 5c0799c9a..7d5d7f8be 100644 --- a/src/Magnum/Implementation/setupDriverWorkarounds.cpp +++ b/src/Magnum/Implementation/setupDriverWorkarounds.cpp @@ -35,8 +35,8 @@ void Context::setupDriverWorkarounds() { #ifndef MAGNUM_TARGET_GLES /* This extension causes crash in GLSL compiler on AMD linux drivers 13.251 */ - const std::string renderer = rendererString(); - if(renderer.find("Advanced Micro Devices") != std::string::npos) + const std::string vendor = vendorString(); + if(vendor.find("ATI Technologies Inc.") != std::string::npos) _setRequiredVersion(GL::ARB::explicit_uniform_location, None); #endif @@ -79,6 +79,7 @@ void Context::setupDriverWorkarounds() { _setRequiredVersion(GL::NV::draw_buffers, None); _setRequiredVersion(GL::NV::fbo_color_attachments, None); // ?? _setRequiredVersion(GL::NV::read_buffer, None); + _setRequiredVersion(GL::NV::framebuffer_blit, None); _setRequiredVersion(GL::NV::framebuffer_multisample, None); _setRequiredVersion(GL::OES::texture_3D, None); _setRequiredVersion(GL::OES::vertex_array_object, None); diff --git a/src/Magnum/Magnum.h b/src/Magnum/Magnum.h index 48b868124..33b68db6f 100644 --- a/src/Magnum/Magnum.h +++ b/src/Magnum/Magnum.h @@ -100,7 +100,7 @@ Defined if the engine is built for OpenGL ES 3.0 or OpenGL ES 2.0. #undef MAGNUM_TARGET_GLES /** -@brief OpenGL ES 2.0 target. +@brief OpenGL ES 2.0 target Defined if the engine is built for OpenGL ES 2.0. Implies also @ref MAGNUM_TARGET_GLES. @@ -110,7 +110,7 @@ Defined if the engine is built for OpenGL ES 2.0. Implies also #undef MAGNUM_TARGET_GLES2 /** -@brief OpenGL ES 3.0 target. +@brief OpenGL ES 3.0 target Defined if the engine is built for OpenGL ES 3.0. Implies also @ref MAGNUM_TARGET_GLES. @@ -124,10 +124,24 @@ Defined if the engine is built for OpenGL ES 3.0. Implies also Defined if the engine is built for OpenGL ES 3.0 or OpenGL ES 2.0 emulated within standard desktop OpenGL. Implies also @ref MAGNUM_TARGET_GLES. -@see @ref MAGNUM_TARGET_GLES2, @ref building +@see @ref MAGNUM_TARGET_GLES2, @ref MAGNUM_TARGET_GLES3, @ref building */ #define MAGNUM_TARGET_DESKTOP_GLES #undef MAGNUM_TARGET_DESKTOP_GLES + +/** +@brief WebGL target + +Defined if the engine is built for WebGL (using Emscripten). WebGL is nearly +equivalent to OpenGL ES 2.0, thus in most cases you don't need to treat it +differently, but there are some +[specific restrictions and features](http://www.khronos.org/registry/webgl/specs/latest/1.0/#6) +which you might want to be aware of. Implies also @ref MAGNUM_TARGET_GLES and +@ref MAGNUM_TARGET_GLES2. +@see @ref CORRADE_TARGET_EMSCRIPTEN, @ref building +*/ +#define MAGNUM_TARGET_WEBGL +#undef MAGNUM_TARGET_WEBGL #endif /** @{ @name Basic type definitions @@ -153,11 +167,21 @@ typedef std::uint32_t UnsignedInt; /** @brief Signed int (32bit) */ typedef std::int32_t Int; -/** @brief Unsigned long (64bit) */ +#ifndef MAGNUM_TARGET_WEBGL +/** +@brief Unsigned long (64bit) + +@attention 64-bit integers are not available in @ref MAGNUM_TARGET_WEBGL "WebGL". +*/ typedef std::uint64_t UnsignedLong; -/** @brief Signed long (64bit) */ +/** +@brief Signed long (64bit) + +@attention 64-bit integers are not available in @ref MAGNUM_TARGET_WEBGL "WebGL". +*/ typedef std::int64_t Long; +#endif /** @brief Float (32bit) */ typedef float Float; @@ -210,14 +234,6 @@ typedef Math::Matrix2x2 Matrix2x2; typedef Math::Matrix<2, Float> Matrix2x2; #endif -#ifdef MAGNUM_BUILD_DEPRECATED -/** -@copybrief Matrix2x2 -@deprecated Use @ref Magnum::Matrix2x2 "Matrix2x2" instead. -*/ -typedef Math::Matrix<2, Float> Matrix2; -#endif - /** @brief 3x3 float matrix @@ -387,14 +403,6 @@ typedef Math::Matrix2x2 Matrix2x2d; typedef Math::Matrix<2, Double> Matrix2x2d; #endif -#ifdef MAGNUM_BUILD_DEPRECATED -/** -@copybrief Matrix2x2d -@deprecated Use @ref Magnum::Matrix2x2d "Matrix2x2d" instead. -*/ -typedef CORRADE_DEPRECATED("use Matrix2x2d instead") Math::Matrix<2, Double> Matrix2d; -#endif - /** @brief 3x3 double matrix @@ -560,9 +568,6 @@ typedef BasicColor4 Color4ub; #ifndef CORRADE_GCC45_COMPATIBILITY enum class ColorFormat: GLenum; enum class ColorType: GLenum; -/** @todo Remove this when dropping backward compatibility */ -typedef ColorFormat ImageFormat; -typedef ColorType ColorType; #endif class Context; @@ -596,6 +601,7 @@ class Mesh; class MeshView; #ifndef MAGNUM_TARGET_GLES +/* MultisampleTextureSampleLocations enum used only in the function */ template class MultisampleTexture; typedef MultisampleTexture<2> MultisampleTexture2D; typedef MultisampleTexture<3> MultisampleTexture2DArray; diff --git a/src/Magnum/Math/Angle.h b/src/Magnum/Math/Angle.h index e3666456d..b42028074 100644 --- a/src/Magnum/Math/Angle.h +++ b/src/Magnum/Math/Angle.h @@ -98,7 +98,7 @@ The requirement of explicit conversions from and to unitless types helps to reduce unit-based errors. Consider following example with implicit conversions allowed: @code -Float std::sin(Float angle); +namespace std { float sin(float angle); } Float sine(Rad angle); Float a = 60.0f; // degrees @@ -110,13 +110,13 @@ std::sin(b); // silent error, std::sin() expected radians These silent errors are easily avoided by requiring explicit conversions: @code -//sine(angleInDegrees); // compilation error -sine(Deg(angleInDegrees)); // explicitly specifying unit +//sine(a); // compilation error +sine(Deg{a}); // explicitly specifying unit -//std::sin(angleInDegrees); // compilation error -std::sin(Float(Rad(angleInDegrees)); // required explicit conversion hints - // to user that this case needs special - // attention (i.e., conversion to radians) +//std::sin(b); // compilation error +std::sin(Float(Rad(b)); // required explicit conversion hints to user + // that this case needs special attention + // (i.e., conversion to radians) @endcode @see Magnum::Deg, Magnum::Degd diff --git a/src/Magnum/Math/Complex.h b/src/Magnum/Math/Complex.h index 5cc5b988a..e510a3e09 100644 --- a/src/Magnum/Math/Complex.h +++ b/src/Magnum/Math/Complex.h @@ -343,8 +343,8 @@ template class Complex { * @see isNormalized() */ T length() const { - /** @todo Remove when NaCl's newlib has this fixed */ - #ifndef CORRADE_TARGET_NACL_NEWLIB + /** @todo Remove when newlib has this fixed */ + #if !defined(CORRADE_TARGET_NACL_NEWLIB) && !defined(CORRADE_TARGET_ANDROID) return std::hypot(_real, _imaginary); #else return std::sqrt(dot()); diff --git a/src/Magnum/Math/Functions.h b/src/Magnum/Math/Functions.h index ec3fb5ee4..8146ce563 100644 --- a/src/Magnum/Math/Functions.h +++ b/src/Magnum/Math/Functions.h @@ -252,7 +252,8 @@ template Vector floor(const Vector& template inline T round(const T& a); #else template inline typename std::enable_if::value, T>::type round(T a) { - #ifndef CORRADE_TARGET_NACL_NEWLIB + /** @todo Remove when newlib has this fixed */ + #if !defined(CORRADE_TARGET_NACL_NEWLIB) && !defined(CORRADE_TARGET_ANDROID) return std::round(a); #else return (a > T(0)) ? std::floor(a + T(0.5)) : std::ceil(a - T(0.5)); @@ -261,7 +262,7 @@ template inline typename std::enable_if::value, T template Vector round(const Vector& a) { Vector out; for(std::size_t i = 0; i != size; ++i) { - #ifndef CORRADE_TARGET_NACL_NEWLIB + #if !defined(CORRADE_TARGET_NACL_NEWLIB) && !defined(CORRADE_TARGET_ANDROID) out[i] = std::round(a[i]); #else out[i] = round(a[i]); @@ -397,8 +398,8 @@ Computes and returns @f$ ab + c @f$. template inline T fma(const T& a, const T& b, const T& c); #else template inline typename std::enable_if::value, T>::type fma(T a, T b, T c) { - /** @todo Remove when NaCl's newlib has this fixed */ - #ifndef CORRADE_TARGET_NACL_NEWLIB + /** @todo Remove when newlib has this fixed */ + #if !defined(CORRADE_TARGET_NACL_NEWLIB) && !defined(CORRADE_TARGET_ANDROID) return std::fma(a, b, c); #else return a*b + c; diff --git a/src/Magnum/Math/TypeTraits.h b/src/Magnum/Math/TypeTraits.h index 2890bcda3..1f92c879f 100644 --- a/src/Magnum/Math/TypeTraits.h +++ b/src/Magnum/Math/TypeTraits.h @@ -145,6 +145,7 @@ template<> struct TypeTraits: Implementation::TypeTraitsIntegral { typedef Double FloatingPointType; #endif }; +#ifndef MAGNUM_TARGET_WEBGL template<> struct TypeTraits: Implementation::TypeTraitsIntegral { #ifndef MAGNUM_TARGET_GLES typedef long double FloatingPointType; @@ -155,6 +156,7 @@ template<> struct TypeTraits: Implementation::TypeTraitsIntegral { typedef long double FloatingPointType; #endif }; +#endif /* Floating-point scalar types */ namespace Implementation { diff --git a/src/Magnum/Math/Vector.h b/src/Magnum/Math/Vector.h index 7adda1d58..cbaae047f 100644 --- a/src/Magnum/Math/Vector.h +++ b/src/Magnum/Math/Vector.h @@ -408,7 +408,8 @@ template class Vector { * @brief Multiply vector component-wise * * @see operator*(T) const, operator*=(const Vector&), - * operator*(const Vector&, const Vector&) + * operator*(const Vector&, const Vector&), + * @ref product() */ Vector operator*(const Vector& other) const { return Vector(*this) *= other; diff --git a/src/Magnum/Mesh.cpp b/src/Magnum/Mesh.cpp index 82ecdbc43..131a33942 100644 --- a/src/Magnum/Mesh.cpp +++ b/src/Magnum/Mesh.cpp @@ -148,7 +148,7 @@ Mesh& Mesh::setLabel(const std::string& label) { } Mesh& Mesh::setIndexBuffer(Buffer& buffer, GLintptr offset, IndexType type, UnsignedInt start, UnsignedInt end) { - #if defined(CORRADE_TARGET_NACL) || defined(CORRADE_TARGET_EMSCRIPTEN) + #if defined(CORRADE_TARGET_NACL) || defined(MAGNUM_TARGET_WEBGL) CORRADE_ASSERT(buffer.targetHint() == Buffer::Target::ElementArray, "Mesh::setIndexBuffer(): the buffer has unexpected target hint, expected" << Buffer::Target::ElementArray << "but got" << buffer.targetHint(), *this); #endif @@ -270,7 +270,7 @@ void Mesh::destroyImplementationVAO() { } void Mesh::attributePointerImplementationDefault(const Attribute& attribute) { - #if defined(CORRADE_TARGET_NACL) || defined(CORRADE_TARGET_EMSCRIPTEN) + #if defined(CORRADE_TARGET_NACL) || defined(MAGNUM_TARGET_WEBGL) CORRADE_ASSERT(attribute.buffer->targetHint() == Buffer::Target::Array, "Mesh::addVertexBuffer(): the buffer has unexpected target hint, expected" << Buffer::Target::Array << "but got" << attribute.buffer->targetHint(), ); #endif @@ -279,7 +279,7 @@ void Mesh::attributePointerImplementationDefault(const Attribute& attribute) { } void Mesh::attributePointerImplementationVAO(const Attribute& attribute) { - #if defined(CORRADE_TARGET_NACL) || defined(CORRADE_TARGET_EMSCRIPTEN) + #if defined(CORRADE_TARGET_NACL) || defined(MAGNUM_TARGET_WEBGL) CORRADE_ASSERT(attribute.buffer->targetHint() == Buffer::Target::Array, "Mesh::addVertexBuffer(): the buffer has unexpected target hint, expected" << Buffer::Target::Array << "but got" << attribute.buffer->targetHint(), ); #endif diff --git a/src/Magnum/Mesh.h b/src/Magnum/Mesh.h index 87b6e25c6..bf7f899f1 100644 --- a/src/Magnum/Mesh.h +++ b/src/Magnum/Mesh.h @@ -291,6 +291,11 @@ respective shader, bind required textures (see @ref AbstractShaderProgram-rendering-workflow "AbstractShaderProgram documentation" for more infromation) and call @ref Mesh::draw(). +@section Mesh-webgl-restrictions WebGL restrictions + +@ref MAGNUM_TARGET_WEBGL "WebGL" puts some restrictions on vertex buffer +layout, see @ref addVertexBuffer() for details. + @section Mesh-performance-optimization Performance optimizations If @extension{APPLE,vertex_array_object} (part of OpenGL 3.0), OpenGL ES 3.0 or @@ -553,6 +558,12 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { * mesh, you must ensure it will exist for whole lifetime of the * mesh and delete it afterwards. * + * @attention In @ref MAGNUM_TARGET_WEBGL "WebGL" the data must be + * properly aligned (e.g. all float data must start at addresses + * divisible by four). Also the maximum stride of attribute data + * must be at most 255 bytes. This is not required anywhere else, + * but doing so may have performance benefits. + * * @see @ref maxVertexAttributes(), @ref setPrimitive(), * @ref setVertexCount(), @fn_gl{BindVertexArray}, * @fn_gl{EnableVertexAttribArray}, @fn_gl{BindBuffer}, @@ -635,7 +646,8 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { #ifdef MAGNUM_BUILD_DEPRECATED /** * @copybrief draw(AbstractShaderProgram&) - * @deprecated Use @ref Magnum::Mesh::draw(AbstractShaderProgram&) instead. + * @deprecated Use @ref Magnum::Mesh::draw(AbstractShaderProgram&) "draw(AbstractShaderProgram&)" + * instead. */ void draw() { #ifndef MAGNUM_TARGET_GLES2 @@ -682,28 +694,28 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { #endif /* Computing stride of interleaved vertex attributes */ - template inline static GLsizei strideOfInterleaved(const AbstractShaderProgram::Attribute& attribute, const U&... attributes) { + template static GLsizei strideOfInterleaved(const AbstractShaderProgram::Attribute& attribute, const U&... attributes) { return attribute.vectorSize()*AbstractShaderProgram::Attribute::VectorCount + strideOfInterleaved(attributes...); } - template inline static GLsizei strideOfInterleaved(GLintptr gap, const T&... attributes) { + template static GLsizei strideOfInterleaved(GLintptr gap, const T&... attributes) { return gap + strideOfInterleaved(attributes...); } - inline static GLsizei strideOfInterleaved() { return 0; } + static GLsizei strideOfInterleaved() { return 0; } /* Adding interleaved vertex attributes */ - template inline void addVertexBufferInternal(Buffer& buffer, GLintptr offset, GLsizei stride, const AbstractShaderProgram::Attribute& attribute, const U&... attributes) { + template void addVertexBufferInternal(Buffer& buffer, GLintptr offset, GLsizei stride, const AbstractShaderProgram::Attribute& attribute, const U&... attributes) { addVertexAttribute(buffer, attribute, offset, stride); /* Add size of this attribute to offset for next attribute */ addVertexBufferInternal(buffer, offset+attribute.vectorSize()*AbstractShaderProgram::Attribute::VectorCount, stride, attributes...); } - template inline void addVertexBufferInternal(Buffer& buffer, GLintptr offset, GLsizei stride, GLintptr gap, const T&... attributes) { + template void addVertexBufferInternal(Buffer& buffer, GLintptr offset, GLsizei stride, GLintptr gap, const T&... attributes) { /* Add the gap to offset for next attribute */ addVertexBufferInternal(buffer, offset+gap, stride, attributes...); } - inline void addVertexBufferInternal(Buffer&, GLsizei, GLintptr) {} + void addVertexBufferInternal(Buffer&, GLsizei, GLintptr) {} - template inline void addVertexAttribute(typename std::enable_if::Type, Float>::value, Buffer&>::type buffer, const AbstractShaderProgram::Attribute& attribute, GLintptr offset, GLsizei stride) { + template void addVertexAttribute(typename std::enable_if::ScalarType, Float>::value, Buffer&>::type buffer, const AbstractShaderProgram::Attribute& attribute, GLintptr offset, GLsizei stride) { for(UnsignedInt i = 0; i != AbstractShaderProgram::Attribute::VectorCount; ++i) attributePointerInternal(Attribute{ &buffer, @@ -717,7 +729,7 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { } #ifndef MAGNUM_TARGET_GLES2 - template inline void addVertexAttribute(typename std::enable_if::Type>::value, Buffer&>::type buffer, const AbstractShaderProgram::Attribute& attribute, GLintptr offset, GLsizei stride) { + template void addVertexAttribute(typename std::enable_if::ScalarType>::value, Buffer&>::type buffer, const AbstractShaderProgram::Attribute& attribute, GLintptr offset, GLsizei stride) { attributePointerInternal(IntegerAttribute{ &buffer, location, @@ -729,7 +741,7 @@ class MAGNUM_EXPORT Mesh: public AbstractObject { } #ifndef MAGNUM_TARGET_GLES - template inline void addVertexAttribute(typename std::enable_if::Type, Double>::value, Buffer&>::type buffer, const AbstractShaderProgram::Attribute& attribute, GLintptr offset, GLsizei stride) { + template void addVertexAttribute(typename std::enable_if::ScalarType, Double>::value, Buffer&>::type buffer, const AbstractShaderProgram::Attribute& attribute, GLintptr offset, GLsizei stride) { for(UnsignedInt i = 0; i != AbstractShaderProgram::Attribute::VectorCount; ++i) attributePointerInternal(LongAttribute{ &buffer, diff --git a/src/Magnum/MeshTools/CMakeLists.txt b/src/Magnum/MeshTools/CMakeLists.txt index f4e9587e2..67f9a9a82 100644 --- a/src/Magnum/MeshTools/CMakeLists.txt +++ b/src/Magnum/MeshTools/CMakeLists.txt @@ -25,6 +25,7 @@ # Files shared between main library and unit test library set(MagnumMeshTools_SRCS + Compile.cpp CompressIndices.cpp FullScreenTriangle.cpp Tipsify.cpp) @@ -37,6 +38,7 @@ set(MagnumMeshTools_GracefulAssert_SRCS set(MagnumMeshTools_HEADERS CombineIndexedArrays.h + Compile.h CompressIndices.h Duplicate.h FlipNormals.h @@ -64,6 +66,7 @@ endif() add_library(MagnumMeshTools ${SHARED_OR_STATIC} $ ${MagnumMeshTools_GracefulAssert_SRCS}) +set_target_properties(MagnumMeshTools PROPERTIES DEBUG_POSTFIX "-d") if(BUILD_STATIC_PIC) # TODO: CMake 2.8.9 has this as POSITION_INDEPENDENT_CODE property set_target_properties(MagnumMeshTools PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") @@ -81,7 +84,9 @@ if(BUILD_TESTS) add_library(MagnumMeshToolsTestLib ${SHARED_OR_STATIC} $ ${MagnumMeshTools_GracefulAssert_SRCS}) - set_target_properties(MagnumMeshToolsTestLib PROPERTIES COMPILE_FLAGS "-DCORRADE_GRACEFUL_ASSERT -DMagnumMeshTools_EXPORTS") + set_target_properties(MagnumMeshToolsTestLib PROPERTIES + COMPILE_FLAGS "-DCORRADE_GRACEFUL_ASSERT -DMagnumMeshTools_EXPORTS" + DEBUG_POSTFIX "-d") target_link_libraries(MagnumMeshToolsTestLib Magnum) # On Windows we need to install first and then run the tests to avoid "DLL diff --git a/src/Magnum/MeshTools/CombineIndexedArrays.cpp b/src/Magnum/MeshTools/CombineIndexedArrays.cpp index 57e918e8b..0de3b868a 100644 --- a/src/Magnum/MeshTools/CombineIndexedArrays.cpp +++ b/src/Magnum/MeshTools/CombineIndexedArrays.cpp @@ -56,26 +56,24 @@ std::pair, std::vector> interleaveAndCombi /* Combine them */ std::vector combinedIndices; - std::tie(combinedIndices, interleavedArrays) = combineIndexArrays(interleavedArrays, stride); + std::tie(combinedIndices, interleavedArrays) = MeshTools::combineIndexArrays(interleavedArrays, stride); return {combinedIndices, interleavedArrays}; } -} - -std::vector combineIndexArrays(const std::initializer_list>> arrays) { +std::vector combineIndexArrays(const std::reference_wrapper>* const begin, const std::reference_wrapper>* const end) { /* Interleave and combine the arrays */ std::vector combinedIndices; std::vector interleavedCombinedArrays; std::tie(combinedIndices, interleavedCombinedArrays) = Implementation::interleaveAndCombineIndexArrays( /* This will bite me hard once. */ - reinterpret_cast>*>(arrays.begin()), - reinterpret_cast>*>(arrays.end())); + reinterpret_cast>*>(begin), + reinterpret_cast>*>(end)); /* Update the original indices */ - const UnsignedInt stride = arrays.size(); + const UnsignedInt stride = end - begin; const UnsignedInt outputSize = interleavedCombinedArrays.size()/stride; for(UnsignedInt offset = 0; offset != stride; ++offset) { - auto& array = (arrays.begin()+offset)->get(); + auto& array = (begin+offset)->get(); CORRADE_INTERNAL_ASSERT(array.size() >= outputSize); array.resize(outputSize); for(UnsignedInt i = 0; i != outputSize; ++i) @@ -85,6 +83,8 @@ std::vector combineIndexArrays(const std::initializer_list, std::vector> combineIndexArrays(const std::vector& interleavedArrays, const UnsignedInt stride) { + CORRADE_ASSERT(stride != 0, "MeshTools::combineIndexArrays(): stride can't be zero", {}); CORRADE_ASSERT(interleavedArrays.size() % stride == 0, "MeshTools::combineIndexArrays(): array size is not divisible by stride", {}); /* Hash map with index combinations, containing just indices into diff --git a/src/Magnum/MeshTools/CombineIndexedArrays.h b/src/Magnum/MeshTools/CombineIndexedArrays.h index c52b72199..4feb71e9a 100644 --- a/src/Magnum/MeshTools/CombineIndexedArrays.h +++ b/src/Magnum/MeshTools/CombineIndexedArrays.h @@ -32,6 +32,7 @@ #include #include #include +#include #include "Magnum/Types.h" #include "Magnum/MeshTools/visibility.h" @@ -42,6 +43,10 @@ namespace Magnum { namespace MeshTools { +namespace Implementation { + MAGNUM_MESHTOOLS_EXPORT std::vector combineIndexArrays(const std::reference_wrapper>* begin, const std::reference_wrapper>* end); +} + /** @brief Combine index arrays @@ -81,7 +86,14 @@ This function calls @ref combineIndexArrays(const std::vector&, Uns internally. See also @ref combineIndexedArrays() which does the vertex data reordering automatically. */ -MAGNUM_MESHTOOLS_EXPORT std::vector combineIndexArrays(std::initializer_list>> arrays); +inline std::vector combineIndexArrays(const std::vector>>& arrays) { + return Implementation::combineIndexArrays(&arrays[0], &arrays[0] + arrays.size()); +} + +/** @overload */ +inline std::vector combineIndexArrays(std::initializer_list>> arrays) { + return Implementation::combineIndexArrays(arrays.begin(), arrays.end()); +} /** @brief Combine index arrays @@ -111,10 +123,14 @@ namespace Implementation { MAGNUM_MESHTOOLS_EXPORT std::pair, std::vector> interleaveAndCombineIndexArrays(const std::reference_wrapper>* begin, const std::reference_wrapper>* end); template void writeCombinedArray(const UnsignedInt stride, const UnsignedInt offset, const std::vector& interleavedCombinedIndexArrays, std::vector& array) { + /* Can't use duplicate() here because we aren't accessing the index data sequentially */ std::vector output; output.reserve(interleavedCombinedIndexArrays.size()/stride); - for(std::size_t i = 0, max = interleavedCombinedIndexArrays.size()/stride; i != max; ++i) - output.push_back(array[interleavedCombinedIndexArrays[offset + i*stride]]); + for(std::size_t i = 0, max = interleavedCombinedIndexArrays.size()/stride; i != max; ++i) { + const UnsignedInt index = interleavedCombinedIndexArrays[offset + i*stride]; + CORRADE_ASSERT(index < array.size(), "MeshTools::combineIndexedArrays(): index out of range", ); + output.push_back(array[index]); + } std::swap(output, array); } diff --git a/src/Magnum/MeshTools/Compile.cpp b/src/Magnum/MeshTools/Compile.cpp new file mode 100644 index 000000000..bc586f77a --- /dev/null +++ b/src/Magnum/MeshTools/Compile.cpp @@ -0,0 +1,157 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + 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 "Compile.h" + +#include "Magnum/Math/Vector3.h" +#include "Magnum/MeshTools/CompressIndices.h" +#include "Magnum/MeshTools/Interleave.h" +#include "Magnum/Trade/MeshData2D.h" +#include "Magnum/Trade/MeshData3D.h" + +/* This header is included only privately and doesn't introduce any linker + dependency, thus it's completely safe */ +#include "Magnum/Shaders/Generic.h" + +namespace Magnum { namespace MeshTools { + +std::tuple, std::unique_ptr> compile(const Trade::MeshData2D& meshData, const BufferUsage usage) { + Mesh mesh; + mesh.setPrimitive(meshData.primitive()); + + /* Decide about stride and offsets */ + UnsignedInt stride = sizeof(Shaders::Generic2D::Position::Type); + const UnsignedInt normalOffset = sizeof(Shaders::Generic2D::Position::Type); + if(meshData.hasTextureCoords2D()) + stride += sizeof(Shaders::Generic2D::TextureCoordinates::Type); + + /* Create vertex buffer */ + std::unique_ptr vertexBuffer{new Buffer{Buffer::Target::Array}}; + + /* Interleave positions */ + std::size_t vertexCount; + Containers::Array data; + std::tie(vertexCount, std::ignore, data) = MeshTools::interleave( + meshData.positions(0), + stride - sizeof(Shaders::Generic2D::Position::Type)); + mesh.addVertexBuffer(*vertexBuffer, 0, + Shaders::Generic2D::Position(), + stride - sizeof(Shaders::Generic2D::Position::Type)); + + /* Add also texture coordinates, if present */ + if(meshData.hasTextureCoords2D()) { + MeshTools::interleaveInto(data, + normalOffset, + meshData.textureCoords2D(0), + stride - normalOffset - sizeof(Shaders::Generic2D::TextureCoordinates::Type)); + mesh.addVertexBuffer(*vertexBuffer, 0, + normalOffset, + Shaders::Generic2D::TextureCoordinates(), + stride - normalOffset - sizeof(Shaders::Generic2D::TextureCoordinates::Type)); + } + + /* Fill vertex buffer with interleaved data and finalize mesh + configuration */ + vertexBuffer->setData(data, BufferUsage::StaticDraw); + mesh.setVertexCount(vertexCount); + + /* Fill index buffer */ + std::unique_ptr indexBuffer; + if(meshData.isIndexed()) { + indexBuffer.reset(new Buffer{Buffer::Target::ElementArray}); + MeshTools::compressIndices(mesh, *indexBuffer, usage, meshData.indices()); + } + + return std::make_tuple(std::move(mesh), std::move(vertexBuffer), std::move(indexBuffer)); +} + +std::tuple, std::unique_ptr> compile(const Trade::MeshData3D& meshData, const BufferUsage usage) { + Mesh mesh; + mesh.setPrimitive(meshData.primitive()); + + /* Decide about stride and offsets */ + UnsignedInt stride = sizeof(Shaders::Generic3D::Position::Type); + const UnsignedInt normalOffset = sizeof(Shaders::Generic3D::Position::Type); + UnsignedInt textureCoordsOffset = sizeof(Shaders::Generic3D::Position::Type); + if(meshData.hasNormals()) { + stride += sizeof(Shaders::Generic3D::Normal::Type); + textureCoordsOffset += sizeof(Shaders::Generic3D::Normal::Type); + } + if(meshData.hasTextureCoords2D()) + stride += sizeof(Shaders::Generic3D::TextureCoordinates::Type); + + /* Create vertex buffer */ + std::unique_ptr vertexBuffer{new Buffer{Buffer::Target::Array}}; + + /* Interleave positions */ + std::size_t vertexCount; + Containers::Array data; + std::tie(vertexCount, std::ignore, data) = MeshTools::interleave( + meshData.positions(0), + stride - sizeof(Shaders::Generic3D::Position::Type)); + mesh.addVertexBuffer(*vertexBuffer, 0, + Shaders::Generic3D::Position(), + stride - sizeof(Shaders::Generic3D::Position::Type)); + + /* Add also normals, if present */ + if(meshData.hasNormals()) { + MeshTools::interleaveInto(data, + normalOffset, + meshData.normals(0), + stride - normalOffset - sizeof(Shaders::Generic3D::Normal::Type)); + mesh.addVertexBuffer(*vertexBuffer, 0, + normalOffset, + Shaders::Generic3D::Normal(), + stride - normalOffset - sizeof(Shaders::Generic3D::Normal::Type)); + } + + /* Add also texture coordinates, if present */ + if(meshData.hasTextureCoords2D()) { + MeshTools::interleaveInto(data, + textureCoordsOffset, + meshData.textureCoords2D(0), + stride - textureCoordsOffset - sizeof(Shaders::Generic3D::TextureCoordinates::Type)); + mesh.addVertexBuffer(*vertexBuffer, 0, + textureCoordsOffset, + Shaders::Generic3D::TextureCoordinates(), + stride - textureCoordsOffset - sizeof(Shaders::Generic3D::TextureCoordinates::Type)); + } + + /* Fill vertex buffer with interleaved data and finalize mesh + configuration */ + vertexBuffer->setData(data, BufferUsage::StaticDraw); + mesh.setVertexCount(vertexCount); + + /* Fill index buffer */ + std::unique_ptr indexBuffer; + if(meshData.isIndexed()) { + indexBuffer.reset(new Buffer{Buffer::Target::ElementArray}); + MeshTools::compressIndices(mesh, *indexBuffer, usage, meshData.indices()); + } + + return std::make_tuple(std::move(mesh), std::move(vertexBuffer), std::move(indexBuffer)); +} + +}} diff --git a/src/Magnum/MeshTools/Compile.h b/src/Magnum/MeshTools/Compile.h new file mode 100644 index 000000000..3cb41c795 --- /dev/null +++ b/src/Magnum/MeshTools/Compile.h @@ -0,0 +1,81 @@ +#ifndef Magnum_MeshTools_Compile_h +#define Magnum_MeshTools_Compile_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + 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 Function @ref Magnum::MeshTools::compile() + */ + +#include +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Trade/Trade.h" +#include "Magnum/MeshTools/visibility.h" + +namespace Magnum { namespace MeshTools { + +/** +@brief Compile 2D mesh data + +Configures mesh for @ref Shaders::Generic2D shader with vertex buffer and +possibly also index buffer, if the mesh is indexed. Positions are bound to +@ref Shaders::Generic2D::Position attribute. If the mesh contains texture +coordinates, they are bound to @ref Shaders::Generic2D::TextureCoordinates +attribute. No data compression or index optimization (except for index buffer +packing) is done. The @p usage parameter is used for both vertex and index +buffer. + +The second returned buffer may be `nullptr` if the mesh is not indexed. + +This is just a convenience function for creating generic meshes, you might want +to use @ref interleave() and @ref compressIndices() functions instead for +greater flexibility. +*/ +MAGNUM_MESHTOOLS_EXPORT std::tuple, std::unique_ptr> compile(const Trade::MeshData2D& meshData, BufferUsage usage); + +/** +@brief Compile 3D mesh data + +Configures mesh for @ref Shaders::Generic3D shader with vertex buffer and +possibly also index buffer, if the mesh is indexed. Positions are bound to +@ref Shaders::Generic3D::Position attribute. If the mesh contains normals, they +are bound to @ref Shaders::Generic3D::Normal attribute, texture coordinates are +bound to @ref Shaders::Generic2D::TextureCoordinates attribute. No data +compression or index optimization (except for index buffer packing) is done. +The @p usage parameter is used for both vertex and index buffer. + +The second returned buffer may be `nullptr` if the mesh is not indexed. + +This is just a convenience function for creating generic meshes, you might want +to use @ref interleave() and @ref compressIndices() functions instead for +greater flexibility. +*/ +MAGNUM_MESHTOOLS_EXPORT std::tuple, std::unique_ptr> compile(const Trade::MeshData3D& meshData, BufferUsage usage); + +}} + +#endif diff --git a/src/Magnum/MeshTools/CompressIndices.h b/src/Magnum/MeshTools/CompressIndices.h index aff27c4b4..bf4a96933 100644 --- a/src/Magnum/MeshTools/CompressIndices.h +++ b/src/Magnum/MeshTools/CompressIndices.h @@ -73,7 +73,7 @@ function writes the output to given buffer and calls @ref Mesh::setIndexCount() and @ref Mesh::setIndexBuffer(), thus you don't need to do anything else for mesh index configuration. -@see @ref MeshTools::interleave() +@see @ref MeshTools::interleave(), @ref MeshTools::compile() */ void MAGNUM_MESHTOOLS_EXPORT compressIndices(Mesh& mesh, Buffer& buffer, BufferUsage usage, const std::vector& indices); diff --git a/src/Magnum/MeshTools/Duplicate.h b/src/Magnum/MeshTools/Duplicate.h index 94fdff10e..4cef1ce8d 100644 --- a/src/Magnum/MeshTools/Duplicate.h +++ b/src/Magnum/MeshTools/Duplicate.h @@ -30,6 +30,7 @@ */ #include +#include #include "Magnum/Types.h" @@ -45,8 +46,10 @@ index array `{1, 1, 0, 3, 2, 2}` will be converted to `{b, b, a, d, c, c}`. template std::vector duplicate(const std::vector& indices, const std::vector& data) { std::vector out; out.reserve(indices.size()); - for(auto it = indices.begin(); it != indices.end(); ++it) + for(auto it = indices.begin(); it != indices.end(); ++it) { + CORRADE_ASSERT(*it < data.size(), "MeshTools::duplicate(): index out of range", out); out.push_back(data[*it]); + } return out; } diff --git a/src/Magnum/MeshTools/Interleave.h b/src/Magnum/MeshTools/Interleave.h index 69879f1f6..d14913d5d 100644 --- a/src/Magnum/MeshTools/Interleave.h +++ b/src/Magnum/MeshTools/Interleave.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Function @ref Magnum::MeshTools::interleave() + * @brief Function @ref Magnum::MeshTools::interleave(), @ref Magnum::MeshTools::interleaveInto() */ #include @@ -41,93 +41,52 @@ namespace Magnum { namespace MeshTools { namespace Implementation { -class Interleave { - public: - Interleave(): _attributeCount(0), _stride(0) {} - - template std::tuple> operator()(const T&... attributes) { - /* Compute buffer size and stride */ - _attributeCount = attributeCount(attributes...); - Containers::Array data; - if(_attributeCount && _attributeCount != ~std::size_t(0)) { - _stride = stride(attributes...); - - /* Create output buffer */ - data = Containers::Array(_attributeCount*_stride); - - /* Save the data */ - write(data.begin(), attributes...); - } - - return std::make_tuple(_attributeCount, _stride, std::move(data)); - } - - template void operator()(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T&... attributes) { - Containers::Array data; - std::tie(std::ignore, std::ignore, data) = operator()(attributes...); - - mesh.setVertexCount(_attributeCount); - buffer.setData(data, usage); - } - - /* Specialization for only one attribute array */ - template typename std::enable_if::value, void>::type operator()(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T& attribute) { - mesh.setVertexCount(attribute.size()); - buffer.setData(attribute, usage); - } - - template static typename std::enable_if::value, std::size_t>::type attributeCount(const T& first, const U&... next) { - CORRADE_ASSERT(sizeof...(next) == 0 || attributeCount(next...) == first.size() || attributeCount(next...) == ~std::size_t(0), "MeshTools::interleave(): attribute arrays don't have the same length, expected" << first.size() << "but got" << attributeCount(next...), 0); - - return first.size(); - } - - template static std::size_t attributeCount(std::size_t, const T&... next) { - return attributeCount(next...); - } - - template static std::size_t attributeCount(std::size_t) { - return ~std::size_t(0); - } - - template static typename std::enable_if::value, std::size_t>::type stride(const T&, const U&... next) { - return sizeof(typename T::value_type) + stride(next...); - } - - template static std::size_t stride(std::size_t gap, const T&... next) { - return gap + stride(next...); - } - - private: - template void write(char* startingOffset, const T& first, const U&... next) { - write(startingOffset+writeOne(startingOffset, first), next...); - } - - /* Copy data to the buffer */ - template typename std::enable_if::value, std::size_t>::type writeOne(char* startingOffset, const T& attributeList) { - auto it = attributeList.begin(); - for(std::size_t i = 0; i != _attributeCount; ++i, ++it) - std::memcpy(startingOffset+i*_stride, reinterpret_cast(&*it), sizeof(typename T::value_type)); +/* Attribute count, skipping gaps. If the attributes are just gaps, returns + ~std::size_t{0}. It must be in the structure to have proper overload + resolution (the functions would otherwise need to be de-inlined to break + cyclic dependencies) */ +struct AttributeCount { + template typename std::enable_if::value, std::size_t>::type operator()(const T& first, const U&... next) const { + CORRADE_ASSERT(sizeof...(next) == 0 || AttributeCount{}(next...) == first.size() || AttributeCount{}(next...) == ~std::size_t(0), "MeshTools::interleave(): attribute arrays don't have the same length, expected" << first.size() << "but got" << AttributeCount{}(next...), 0); + + return first.size(); + } + template std::size_t operator()(std::size_t, const T& first, const U&... next) const { + return AttributeCount{}(first, next...); + } + constexpr std::size_t operator()(std::size_t) const { return ~std::size_t(0); } + constexpr std::size_t operator()() const { return 0; } +}; - return sizeof(typename T::value_type); - } +/* Stride, taking gaps into account. It must be in the structure, same reason + as above */ +struct Stride { + template typename std::enable_if::value, std::size_t>::type operator()(const T&, const U&... next) const { + return sizeof(typename T::value_type) + Stride{}(next...); + } + template std::size_t operator()(std::size_t gap, const T&... next) const { + return gap + Stride{}(next...); + } + constexpr std::size_t operator()() const { return 0; } +}; - /* Fill gap with zeros */ - std::size_t writeOne(char* startingOffset, std::size_t gap) { - for(std::size_t i = 0; i != _attributeCount; ++i) - std::memset(startingOffset+i*_stride, 0, gap); +/* Copy data to the buffer */ +template typename std::enable_if::value, std::size_t>::type writeOneInterleaved(std::size_t stride, char* startingOffset, const T& attributeList) { + auto it = attributeList.begin(); + for(std::size_t i = 0; i != attributeList.size(); ++i, ++it) + std::memcpy(startingOffset + i*stride, reinterpret_cast(&*it), sizeof(typename T::value_type)); - return gap; - } + return sizeof(typename T::value_type); +} - /* Terminator functions for recursive calls */ - static std::size_t attributeCount() { return 0; } - static std::size_t stride() { return 0; } - void write(char*) {} +/* Skip gap */ +inline constexpr std::size_t writeOneInterleaved(std::size_t, char*, std::size_t gap) { return gap; } - std::size_t _attributeCount; - std::size_t _stride; -}; +/* Write interleaved data */ +inline void writeInterleaved(std::size_t, char*) {} +template void writeInterleaved(std::size_t stride, char* startingOffset, const T& first, const U&... next) { + writeInterleaved(stride, startingOffset + writeOneInterleaved(stride, startingOffset, first), next...); +} } @@ -155,15 +114,15 @@ It's often desirable to align data for one vertex on 32bit boundaries. To achieve that, you can specify gaps between the attributes: @code std::vector positions; -std::vector weights; -std::vector> vertexColors; +std::vector weights; +std::vector vertexColors; std::size_t attributeCount; std::size_t stride; Containers::Array data; std::tie(attributeCount, stride, data) = MeshTools::interleave(positions, weights, 2, textureCoordinates, 1); @endcode -This way vertex stride is 24 bytes, without gaps it would be 21 bytes, causing -possible performance loss. +All gap bytes are set zero. This way vertex stride is 24 bytes, without gaps it +would be 21 bytes, causing possible performance loss. @attention The function expects that all arrays have the same size. @@ -173,15 +132,53 @@ possible performance loss. will be `std::vector` or `std::array`. See also @ref interleave(Mesh&, Buffer&, BufferUsage, const T&...), -which writes the interleaved array directly into buffer of given mesh. +which writes the interleaved array directly into buffer of given mesh or +@ref interleaveInto() which writes the data into existing buffer instead of +creating new one. */ /* enable_if to avoid clash with overloaded function below */ -template inline typename std::enable_if::value, std::tuple>>::type interleave(const T& first, const U&... next) { - return Implementation::Interleave()(first, next...); +template typename std::enable_if::value, std::tuple>>::type interleave(const T& first, const U&... next) { + /* Compute buffer size and stride */ + const std::size_t attributeCount = Implementation::AttributeCount{}(first, next...); + const std::size_t stride = Implementation::Stride{}(first, next...); + + /* Create output buffer only if we have some attributes */ + if(attributeCount && attributeCount != ~std::size_t(0)) { + Containers::Array data = Containers::Array::zeroInitialized(attributeCount*stride); + Implementation::writeInterleaved(stride, data.begin(), first, next...); + + return std::make_tuple(attributeCount, stride, std::move(data)); + + /* Otherwise return nullptr */ + } else return std::make_tuple(0, stride, nullptr); +} + +/** +@brief %Interleave vertex attributes into existing buffer + +Unlike @ref interleave() this function interleaves the data into existing +buffer and leaves gaps untouched instead of zero-initializing them. This +function can thus be used for interleaving data depending on runtime +parameters. + +@attention Similarly to @ref interleave(), this function expects that all + arrays have the same size. The passed buffer must also be large enough to + contain the interleaved data. +*/ +template std::tuple interleaveInto(Containers::ArrayReference buffer, const T& first, const U&... next) { + /* Verify expected buffer size */ + const std::size_t attributeCount = Implementation::AttributeCount{}(first, next...); + const std::size_t stride = Implementation::Stride{}(first, next...); + CORRADE_ASSERT(attributeCount*stride <= buffer.size(), "MeshTools::interleaveInto(): the data buffer is too small, expected" << attributeCount*stride << "but got" << buffer.size(), {}); + + /* Write data */ + Implementation::writeInterleaved(stride, buffer.begin(), first, next...); + + return std::make_tuple(attributeCount, stride); } /** -@brief %Interleave vertex attributes and write them to array buffer +@brief %Interleave vertex attributes, write them to array buffer and configure the mesh @param mesh Output mesh @param buffer Output vertex buffer @param usage Vertex buffer usage @@ -194,18 +191,31 @@ so you don't have to call @ref Mesh::setVertexCount() on your own. @attention You still must call @ref Mesh::setPrimitive() and @ref Mesh::addVertexBuffer() on the mesh afterwards. -For only one attribute array this function is convenient equivalent to the -following, without any performance loss: +@see @ref compressIndices(), @ref compile() +@todo rework so Mesh & Buffer doesn't need to be included in header +*/ +template void interleave(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T&... attributes) { + Containers::Array data; + std::size_t attributeCount; + std::tie(attributeCount, std::ignore, data) = interleave(attributes...); + + mesh.setVertexCount(attributeCount); + buffer.setData(data, usage); +} + +/** +@brief Write vertex attribute to array buffer and configure the mesh + +Simplified specialization of the above function for only one attribute array, +equivalent to the following: @code buffer.setData(attribute, usage); mesh.setVertexCount(attribute.size()); @endcode - -@see @ref MeshTools::compressIndices() -@todo rework so Mesh & Buffer doesn't need to be included in header */ -template inline void interleave(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T&... attributes) { - return Implementation::Interleave()(mesh, buffer, usage, attributes...); +template typename std::enable_if::value, void>::type interleave(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T& attribute) { + mesh.setVertexCount(attribute.size()); + buffer.setData(attribute, usage); } }} diff --git a/src/Magnum/MeshTools/Test/InterleaveTest.cpp b/src/Magnum/MeshTools/Test/InterleaveTest.cpp index a101d29b1..f79bf14b9 100644 --- a/src/Magnum/MeshTools/Test/InterleaveTest.cpp +++ b/src/Magnum/MeshTools/Test/InterleaveTest.cpp @@ -42,6 +42,8 @@ class InterleaveTest: public Corrade::TestSuite::Tester { void strideGaps(); void write(); void writeGaps(); + + void interleaveInto(); }; InterleaveTest::InterleaveTest() { @@ -50,36 +52,38 @@ InterleaveTest::InterleaveTest() { &InterleaveTest::stride, &InterleaveTest::strideGaps, &InterleaveTest::write, - &InterleaveTest::writeGaps}); + &InterleaveTest::writeGaps, + + &InterleaveTest::interleaveInto}); } void InterleaveTest::attributeCount() { std::stringstream ss; Error::setOutput(&ss); - CORRADE_COMPARE((Implementation::Interleave::attributeCount(std::vector{0, 1, 2}, + CORRADE_COMPARE((Implementation::AttributeCount{}(std::vector{0, 1, 2}, std::vector{0, 1, 2, 3, 4, 5})), std::size_t(0)); CORRADE_COMPARE(ss.str(), "MeshTools::interleave(): attribute arrays don't have the same length, expected 3 but got 6\n"); - CORRADE_COMPARE((Implementation::Interleave::attributeCount(std::vector{0, 1, 2}, + CORRADE_COMPARE((Implementation::AttributeCount{}(std::vector{0, 1, 2}, std::vector{3, 4, 5})), std::size_t(3)); } void InterleaveTest::attributeCountGaps() { - CORRADE_COMPARE((Implementation::Interleave::attributeCount(std::vector{0, 1, 2}, 3, + CORRADE_COMPARE((Implementation::AttributeCount{}(std::vector{0, 1, 2}, 3, std::vector{3, 4, 5}, 5)), std::size_t(3)); /* No arrays from which to get size */ - CORRADE_COMPARE(Implementation::Interleave::attributeCount(3, 5), ~std::size_t(0)); + CORRADE_COMPARE(Implementation::AttributeCount{}(3, 5), ~std::size_t(0)); } void InterleaveTest::stride() { - CORRADE_COMPARE(Implementation::Interleave::stride(std::vector()), std::size_t(1)); - CORRADE_COMPARE(Implementation::Interleave::stride(std::vector()), std::size_t(4)); - CORRADE_COMPARE((Implementation::Interleave::stride(std::vector(), std::vector())), std::size_t(5)); + CORRADE_COMPARE(Implementation::Stride{}(std::vector()), std::size_t(1)); + CORRADE_COMPARE(Implementation::Stride{}(std::vector()), std::size_t(4)); + CORRADE_COMPARE((Implementation::Stride{}(std::vector(), std::vector())), std::size_t(5)); } void InterleaveTest::strideGaps() { - CORRADE_COMPARE((Implementation::Interleave::stride(2, std::vector(), 1, std::vector(), 12)), std::size_t(20)); + CORRADE_COMPARE((Implementation::Stride{}(2, std::vector(), 1, std::vector(), 12)), std::size_t(20)); } void InterleaveTest::write() { @@ -137,6 +141,41 @@ void InterleaveTest::writeGaps() { } } +void InterleaveTest::interleaveInto() { + std::size_t attributeCount; + std::size_t stride; + auto data = Containers::Array::from( + 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, + 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, + 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, + 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77, 0x11, 0x33, 0x55, 0x77 + ); + + std::tie(attributeCount, stride) = MeshTools::interleaveInto(data, + 2, std::vector{4, 5, 6, 7}, 1, std::vector{0, 1, 2, 3}, 3); + + CORRADE_COMPARE(attributeCount, std::size_t{4}); + CORRADE_COMPARE(stride, std::size_t{12}); + + if(!Utility::Endianness::isBigEndian()) { + /* _______gap, int___________________, _gap, short_____, _____________gap */ + CORRADE_COMPARE(std::vector(data.begin(), data.end()), (std::vector{ + 0x11, 0x33, 0x04, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x33, 0x55, 0x77, + 0x11, 0x33, 0x05, 0x00, 0x00, 0x00, 0x55, 0x01, 0x00, 0x33, 0x55, 0x77, + 0x11, 0x33, 0x06, 0x00, 0x00, 0x00, 0x55, 0x02, 0x00, 0x33, 0x55, 0x77, + 0x11, 0x33, 0x07, 0x00, 0x00, 0x00, 0x55, 0x03, 0x00, 0x33, 0x55, 0x77 + })); + } else { + /* _______gap, ___________________int, _gap, _____short, _____________gap */ + CORRADE_COMPARE(std::vector(data.begin(), data.end()), (std::vector{ + 0x11, 0x33, 0x00, 0x00, 0x00, 0x04, 0x55, 0x00, 0x00, 0x33, 0x55, 0x77, + 0x11, 0x33, 0x00, 0x00, 0x00, 0x05, 0x55, 0x00, 0x01, 0x33, 0x55, 0x77, + 0x11, 0x33, 0x00, 0x00, 0x00, 0x06, 0x55, 0x00, 0x02, 0x33, 0x55, 0x77, + 0x11, 0x33, 0x00, 0x00, 0x00, 0x07, 0x55, 0x00, 0x03, 0x33, 0x55, 0x77 + })); + } +} + }}} CORRADE_TEST_MAIN(Magnum::MeshTools::Test::InterleaveTest) diff --git a/src/Magnum/MeshView.h b/src/Magnum/MeshView.h index 4b137ea17..d283d6410 100644 --- a/src/Magnum/MeshView.h +++ b/src/Magnum/MeshView.h @@ -126,7 +126,8 @@ class MAGNUM_EXPORT MeshView { #ifdef MAGNUM_BUILD_DEPRECATED /** * @copybrief draw(AbstractShaderProgram&) - * @deprecated Use + * @deprecated Use @ref Magnum::MeshView::draw(AbstractShaderProgram&) "draw(AbstractShaderProgram&)" + * instead. */ CORRADE_DEPRECATED("use draw(AbstractShaderProgram&) instead") void draw(); #endif diff --git a/src/Magnum/MultisampleTexture.h b/src/Magnum/MultisampleTexture.h index 97c986b6d..05d54a2c3 100644 --- a/src/Magnum/MultisampleTexture.h +++ b/src/Magnum/MultisampleTexture.h @@ -44,6 +44,16 @@ namespace Implementation { template<> inline constexpr GLenum multisampleTextureTarget<3>() { return GL_TEXTURE_2D_MULTISAMPLE_ARRAY; } } +/** +@brief Multisample texture sample locations + +@see @ref MultisampleTexture::setStorage() +*/ +enum class MultisampleTextureSampleLocations: GLboolean { + NotFixed = GL_FALSE, + Fixed = GL_TRUE +}; + /** @brief Mulitsample texture @@ -77,19 +87,67 @@ template class MultisampleTexture: public AbstractTextur */ explicit MultisampleTexture(): AbstractTexture(Implementation::multisampleTextureTarget()) {} - #ifndef MAGNUM_TARGET_GLES - /** @copydoc Texture::imageSize() */ - typename DimensionTraits::VectorType imageSize(Int level) { - return DataHelper::imageSize(*this, _target, level); + /** + * @brief %Image size + * + * The result is not cached in any way. If + * @extension{EXT,direct_state_access} is not available, the texture + * is bound to some texture unit before the operation. + * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and + * @fn_gl{GetTexLevelParameter} or @fn_gl_extension{GetTextureLevelParameter,EXT,direct_state_access} + * with @def_gl{TEXTURE_WIDTH}, @def_gl{TEXTURE_HEIGHT} or + * @def_gl{TEXTURE_DEPTH} + */ + typename DimensionTraits::VectorType imageSize() { + return DataHelper::imageSize(*this, _target, 0); + } + + /** + * @brief Set storage + * @param samples Sample count + * @param internalFormat Internal format + * @param size %Texture size + * @param sampleLocations Whether to use fixed sample locations + * @return Reference to self (for method chaining) + * + * After calling this function the texture is immutable and calling + * @ref setStorage() again is not allowed. + * + * If @extension{EXT,direct_state_access} is not available, the texture + * is bound to some texture unit before the operation. If + * @extension{ARB,texture_storage_multisample} (part of OpenGL 4.3) is + * not available, the feature is emulated using plain + * @extension{ARB,texture_storage} functionality (which unfortunately + * doesn't have any DSA alternative, so the texture must be bound + * to some texture unit before). + * @see @ref maxColorSamples(), @ref maxDepthSamples(), + * @ref maxIntegerSamples(), @fn_gl{ActiveTexture}, @fn_gl{BindTexture} + * and @fn_gl{TexStorage2DMultisample}/@fn_gl{TexStorage3DMultisample} + * or @fn_gl_extension{TextureStorage2DMultisample,EXT,direct_state_access}/ + * @fn_gl_extension{TextureStorage3DMultisample,EXT,direct_state_access} + * eventually @fn_gl{TexImage2DMultisample}/@fn_gl{TexImage3DMultisample} + * @todoc Remove the workaround when it stops breaking Doxygen layout so badly + */ + /* The default parameter value was chosen based on discussion in + ARB_texture_multisample specs (fixed locations is treated as the + special case) */ + MultisampleTexture& setStorage(Int samples, TextureFormat internalFormat, const typename DimensionTraits::VectorType& size, MultisampleTextureSampleLocations sampleLocations = + #ifndef DOXYGEN_GENERATING_OUTPUT + MultisampleTextureSampleLocations::NotFixed + #else + NotFixed + #endif + ) { + DataHelper::setStorageMultisample(*this, _target, samples, internalFormat, size, GLboolean(sampleLocations)); + return *this; } - #endif - /** @copydoc Texture::invalidateImage() */ - void invalidateImage(Int level) { AbstractTexture::invalidateImage(level); } + /** @copydoc RectangleTexture::invalidateImage() */ + void invalidateImage() { AbstractTexture::invalidateImage(0); } - /** @copydoc Texture::invalidateSubImage() */ - void invalidateSubImage(Int level, const typename DimensionTraits::VectorType& offset, const typename DimensionTraits::VectorType& size) { - DataHelper::invalidateSubImage(*this, level, offset, size); + /** @copydoc RectangleTexture::invalidateSubImage() */ + void invalidateSubImage(const typename DimensionTraits::VectorType& offset, const typename DimensionTraits::VectorType& size) { + DataHelper::invalidateSubImage(*this, 0, offset, size); } /* Overloads to remove WTF-factor from method chaining order */ diff --git a/src/Magnum/Platform/AbstractXApplication.h b/src/Magnum/Platform/AbstractXApplication.h index 6f0646480..1de3a2b3e 100644 --- a/src/Magnum/Platform/AbstractXApplication.h +++ b/src/Magnum/Platform/AbstractXApplication.h @@ -92,7 +92,10 @@ class AbstractXApplication { /** * @brief Execute main loop - * @return Value for returning from `main()`. + * @return Value for returning from `main()` + * + * See @ref MAGNUM_GLXAPPLICATION_MAIN() or + * @ref MAGNUM_XEGLAPPLICATION_MAIN() for usage information. */ int exec(); @@ -268,33 +271,6 @@ class AbstractXApplication::InputEvent { Ctrl = ControlMask, /**< Ctrl */ Alt = Mod1Mask, /**< Alt */ AltGr = Mod5Mask, /**< AltGr */ - - #ifdef MAGNUM_BUILD_DEPRECATED - /** - * @copybrief Button::Left - * @deprecated Use @ref Magnum::Platform::AbstractXApplication::InputEvent::buttons() "buttons()" - * and @ref Magnum::Platform::AbstractXApplication::InputEvent::Button::Left "Button::Left" - * instead. - */ - LeftButton = Button1Mask, - - /** - * @copybrief Button::Middle - * @deprecated Use @ref Magnum::Platform::AbstractXApplication::InputEvent::buttons() "buttons()" - * and @ref Magnum::Platform::AbstractXApplication::InputEvent::Button::Middle "Button::Middle" - * instead. - */ - MiddleButton = Button2Mask, - - /** - * @copybrief Button::Right - * @deprecated Use @ref Magnum::Platform::AbstractXApplication::InputEvent::buttons() "buttons()" - * and @ref Magnum::Platform::AbstractXApplication::InputEvent::Button::Right "Button::Right" - * instead. - */ - RightButton = Button3Mask, - #endif - CapsLock = LockMask, /**< Caps lock */ NumLock = Mod2Mask /**< Num lock */ }; diff --git a/src/Magnum/Platform/AndroidApplication.cpp b/src/Magnum/Platform/AndroidApplication.cpp new file mode 100644 index 000000000..436b02da1 --- /dev/null +++ b/src/Magnum/Platform/AndroidApplication.cpp @@ -0,0 +1,266 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + 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 "AndroidApplication.h" + +#include +#include + +#include "Magnum/Context.h" + +#include "Implementation/Egl.h" + +namespace Magnum { namespace Platform { + +/** @todo Delegating constructors when support for GCC 4.6 can be dropped */ + +AndroidApplication::AndroidApplication(const Arguments& arguments, const Configuration& configuration): _state(arguments) { + initialize(); + createContext(configuration); +} + +#ifndef DOXYGEN_GENERATING_OUTPUT +AndroidApplication::AndroidApplication(const Arguments& arguments): _state(arguments) { + initialize(); + createContext(); +} +#endif + +AndroidApplication::AndroidApplication(const Arguments& arguments, std::nullptr_t): _state(arguments) { + initialize(); +} + +AndroidApplication::~AndroidApplication() { + eglMakeCurrent(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(_display, _context); + eglDestroySurface(_display, _surface); + eglTerminate(_display); +} + +struct AndroidApplication::LogOutput { + LogOutput(); + + Utility::AndroidLogStreamBuffer debugBuffer, warningBuffer, errorBuffer; + std::ostream debugStream, warningStream, errorStream; +}; + +AndroidApplication::LogOutput::LogOutput(): + debugBuffer(Utility::AndroidLogStreamBuffer::LogPriority::Info, "magnum"), + warningBuffer(Utility::AndroidLogStreamBuffer::LogPriority::Warning, "magnum"), + errorBuffer(Utility::AndroidLogStreamBuffer::LogPriority::Error, "magnum"), + debugStream(&debugBuffer), warningStream(&warningBuffer), errorStream(&errorBuffer) +{ + Debug::setOutput(&debugStream); + Warning::setOutput(&warningStream); + Error::setOutput(&errorStream); +} + +void AndroidApplication::initialize() { + /* Redirect debug output to Android log */ + _logOutput.reset(new LogOutput); +} + +void AndroidApplication::createContext() { createContext({}); } + +void AndroidApplication::createContext(const Configuration& configuration) { + if(!tryCreateContext(configuration)) std::exit(32); +} + +bool AndroidApplication::tryCreateContext(const Configuration& configuration) { + /* Initialize EGL */ + _display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if(!eglInitialize(_display, nullptr, nullptr)) { + Error() << "Platform::AndroidApplication::tryCreateContext(): cannot initialize EGL:" + << Implementation::eglErrorString(eglGetError()); + return false; + } + + /* Choose config */ + const EGLint configAttributes[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_RED_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + EGLint configCount; + EGLConfig config; + if(!eglChooseConfig(_display, configAttributes, &config, 1, &configCount)) { + Error() << "Platform::AndroidApplication::tryCreateContext(): cannot choose EGL config:" + << Implementation::eglErrorString(eglGetError()); + return false; + } + + /* Resize native window and match it to the selected format */ + EGLint format; + CORRADE_INTERNAL_ASSERT_OUTPUT(eglGetConfigAttrib(_display, config, EGL_NATIVE_VISUAL_ID, &format)); + ANativeWindow_setBuffersGeometry(_state->window, + configuration.size().isZero() ? 0 : configuration.size().x(), + configuration.size().isZero() ? 0 : configuration.size().y(), format); + + /* Create surface and context */ + if(!(_surface = eglCreateWindowSurface(_display, config, _state->window, nullptr))) { + Error() << "Platform::AndroidApplication::tryCreateContext(): cannot create EGL window surface:" + << Implementation::eglErrorString(eglGetError()); + return false; + } + const EGLint contextAttributes[] = { + #ifdef MAGNUM_TARGET_GLES2 + EGL_CONTEXT_CLIENT_VERSION, 2, + #elif defined(MAGNUM_TARGET_GLES3) + EGL_CONTEXT_CLIENT_VERSION, 3, + #else + #error Android with desktop OpenGL? Wow, that is a new thing. + #endif + EGL_NONE + }; + if(!(_context = eglCreateContext(_display, config, EGL_NO_CONTEXT, contextAttributes))) { + Error() << "Platform::AndroidApplication::tryCreateContext(): cannot create EGL context:" + << Implementation::eglErrorString(eglGetError()); + return false; + } + + /* Make the context current */ + CORRADE_INTERNAL_ASSERT_OUTPUT(eglMakeCurrent(_display, _surface, _surface, _context)); + + _c.reset(new Context); + return true; +} + +void AndroidApplication::swapBuffers() { + eglSwapBuffers(_display, _surface); +} + +void AndroidApplication::viewportEvent(const Vector2i&) {} +void AndroidApplication::mousePressEvent(MouseEvent&) {} +void AndroidApplication::mouseReleaseEvent(MouseEvent&) {} +void AndroidApplication::mouseMoveEvent(MouseMoveEvent&) {} + +namespace { + struct Data { + Data(std::unique_ptr(*instancer)(const AndroidApplication::Arguments&)): instancer(instancer) {} + + std::unique_ptr(*instancer)(const AndroidApplication::Arguments&); + std::unique_ptr instance; + }; +} + +void AndroidApplication::commandEvent(android_app* state, int32_t cmd) { + Data& data = *static_cast(state->userData); + + switch (cmd) { + case APP_CMD_SAVE_STATE: + /** @todo Make use of this */ + break; + + case APP_CMD_INIT_WINDOW: + /* Create the application */ + if(!data.instance) { + data.instance = data.instancer(state); + data.instance->drawEvent(); + } + break; + + case APP_CMD_TERM_WINDOW: + /* Destroy the application */ + data.instance.reset(); + break; + + case APP_CMD_GAINED_FOCUS: + case APP_CMD_LOST_FOCUS: + /** @todo Make use of these */ + break; + } +} + +std::int32_t AndroidApplication::inputEvent(android_app* state, AInputEvent* event) { + CORRADE_INTERNAL_ASSERT(static_cast(state->userData)->instance); + AndroidApplication& app = *static_cast(state->userData)->instance; + if(AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { + const std::int32_t action = AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK; + switch(action) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_UP: { + MouseEvent e(event); + action == AMOTION_EVENT_ACTION_DOWN ? app.mousePressEvent(e) : app.mouseReleaseEvent(e); + return e.isAccepted() ? 1 : 0; + } + + case AMOTION_EVENT_ACTION_MOVE: { + MouseMoveEvent e(event); + app.mouseMoveEvent(e); + return e.isAccepted() ? 1 : 0; + } + } + + /** @todo Implement also other input events */ + } + + return 0; +} + +void AndroidApplication::exec(android_app* state, std::unique_ptr(*instancer)(const Arguments&)) { + state->onAppCmd = commandEvent; + state->onInputEvent = inputEvent; + + /* Make sure the glue isn't stripped. WHY WHYYY CAN'T THIS BE DONE SOME + SANE WAY WHYY */ + app_dummy(); + + /** @todo Make use of saved state */ + Data data{instancer}; + state->userData = &data; + + for(;;) { + /* Read all pending events. Block and wait for them only if the app + doesn't want to redraw immediately WHY THIS GODDAMN THING DOESNT + HAVE SOMETHING LIKE WAIT FOR EVENT SO I NEED TO TANGLE THIS TANGLED + MESS OF HELL */ + int ident, events; + android_poll_source* source; + while((ident = ALooper_pollAll( + data.instance && (data.instance->_flags & Flag::Redraw) ? 0 : -1, + nullptr, &events, reinterpret_cast(&source))) >= 0) + { + /* Process this event OH SIR MAY MY POOR EXISTENCE CALL THIS + FUNCTION FOR YOU IF YOU DON'T MIND? */ + if(source) source->process(state, source); + + /* Exit WHY THIS HAS TO BE HANDLED HERE WHILE EVERY OTHER THING + IS HANDLED THROUGH CALLBACK GODDAMMIT */ + if(state->destroyRequested != 0) return; + } + + /* Redraw the app if it wants to be redrawn. Frame limiting is done by + Android itself */ + if(data.instance && (data.instance->_flags & Flag::Redraw)) + data.instance->drawEvent(); + } + + state->userData = nullptr; +} + +}} diff --git a/src/Magnum/Platform/AndroidApplication.h b/src/Magnum/Platform/AndroidApplication.h new file mode 100644 index 000000000..38d482c4b --- /dev/null +++ b/src/Magnum/Platform/AndroidApplication.h @@ -0,0 +1,594 @@ +#ifndef Magnum_Platform_AndroidApplication_h +#define Magnum_Platform_AndroidApplication_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + 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::Platform::AndroidApplication + */ + +#include +#include +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Math/Vector2.h" +#include "Magnum/Platform/Platform.h" + +#ifndef CORRADE_TARGET_ANDROID +#error This file is available only on Android +#endif + +/* Undef Xlib nonsense which might get pulled in by EGL */ +#undef None + +namespace Magnum { namespace Platform { + +/** @nosubgrouping +@brief Android application + +Application running in Android. + +This application library is available only in +@ref CORRADE_TARGET_ANDROID "Android", see respective sections +in @ref building-corrade-cross-android "Corrade's" and @ref building-cross-android "Magnum's" +building documentation. It is built if `WITH_ANDROIDAPPLICATION` is enabled in +CMake. + +## Bootstrap application + +Fully contained base application using @ref GlutApplication for desktop build +and @ref AndroidApplication for Android build along with full Android packaging +stuff and CMake setup is available in `base-android` branch of +[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) repository, +download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/base-android.tar.gz) +or [zip](https://github.com/mosra/magnum-bootstrap/archive/base-android.zip) file. +After extracting the downloaded archive, you can do the desktop build in the +same way as with @ref GlutApplication. For the Android build you also +need to put the contents of toolchains repository from https://github.com/mosra/toolchains +in `toolchains/` subdirectory. Don't forget to adapt `ANDROID_NDK_ROOT` in +`toolchains/generic/Android-*.cmake` to path where NDK is installed. Default is +`/opt/android-ndk`. Adapt also `ANDROID_SYSROOT` to your preferred API level. +You might also need to update `ANDROID_TOOLCHAIN_PREFIX` and +`ANDROID_TOOLCHAIN_ROOT` to fit your system. + +First you need to update Android project files with the following command. It +will create `build.xml` file for Ant and a bunch of other files. You need to +specify the target for which you will build in the `-t` parameter. List of all +targets can be obtained by calling `android list target`. + + android update project -p . -t "android-19" + +Then create build directories for ARM and x86 and run `cmake` and build command +in them. The toolchains need access to the platform file, so be sure to +properly set **absolute** path to `toolchains/modules/` directory containing +`Platform/Android.cmake`. + + mkdir build-android-arm && cd build-android-arm + cmake .. \ + -DCMAKE_MODULE_PATH="/absolute/path/to/toolchains/modules" \ + -DCMAKE_TOOLCHAIN_FILE="../toolchains/generic/Android-ARM.cmake" + cmake --build . + + mkdir build-android-x86 && cd build-android-x86 + cmake .. \ + -DCMAKE_MODULE_PATH="/absolute/path/to/toolchains/modules" \ + -DCMAKE_TOOLCHAIN_FILE="../toolchains/generic/Android-x86.cmake" + cmake --build . + +The compiled binaries will be put into `lib/armeabi-v7a` and `lib/x86`. You can +then build the APK package simply by running `ant`. The resulting APK package +can be then installed directly on the device or emulator using `adb install`. + + ant debug + adb install bin/NativeActivity-debug.apk + +## General usage + +For CMake you need to copy `FindEGL.cmake` and `FindOpenGLES2.cmake` (or +`FindOpenGLES3.cmake`) from `modules/` directory in %Magnum source to `modules/` +dir in your project (so it is able to find EGL and OpenGL ES libraries). +Request `%AndroidApplication` component, add +`${MAGNUM_ANDROIDAPPLICATION_INCLUDE_DIRS}` to include path and link to +`${MAGNUM_ANDROIDAPPLICATION_LIBRARIES}`. If no other application is requested, +you can also use generic `${MAGNUM_APPLICATION_INCLUDE_DIRS}` and +`${MAGNUM_APPLICATION_LIBRARIES}` aliases to simplify porting. See +@ref building and @ref cmake for more information. Note that unlike on other +platforms you need to create *shared library* instead of executable. The +resulting binary then needs to be copied to `lib/armeabi-v7a` and `lib/x86`, +you can do that automatically in CMake using the following commands: + + file(MAKE_DIRECTORY "${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}") + +In C++ code you need to implement at least @ref drawEvent() to be able to draw +on the screen. The subclass must be then made accessible from JNI using +@ref MAGNUM_ANDROIDAPPLICATION_MAIN() macro. See @ref platform for more +information. +@code +class MyApplication: public Platform::AndroidApplication { + // implement required methods... +}; +MAGNUM_ANDROIDAPPLICATION_MAIN(MyApplication) +@endcode + +If no other application header is included, this class is also aliased to +`Platform::Application` and the macro is aliased to `MAGNUM_APPLICATION_MAIN()` +to simplify porting. + +### Android packaging stuff + +The application needs at least the `AndroidManifest.xml` with the following +contents: + + + + + + + + + + + + + + + + +Modify `android:label` to your liking, set unique `package` name and replace +`{{application}}` with name of the binary file (without extension). If you plan +to use OpenGL ES, set `android:glEsVersion` to `0x00030000`. The resulting APK +file will be named `NativeActivity.apk` by default, you can change that either +by passing `-n` parameter to `android update project` or later by editing first +line of the generated `build.xml` file. + +## Redirecting output to Android log buffer + +The application by default redirects @ref Corrade::Utility::Debug "Debug", +@ref Corrade::Utility::Warning "Warning" and @ref Corrade::Utility::Error "Error" +output to Android log buffer with tag `"magnum"`, which can be then accessed +through `logcat` utility. See also @ref Corrade::Utility::AndroidLogStreamBuffer +for more information. +*/ +class AndroidApplication { + public: + /** @brief Application arguments */ + typedef android_app* Arguments; + + class Configuration; + class InputEvent; + class MouseEvent; + class MouseMoveEvent; + + /** + * @brief Execute the application + * + * See @ref MAGNUM_ANDROIDAPPLICATION_MAIN() for usage information. + */ + static void exec(android_app* state, std::unique_ptr(*instancer)(const Arguments&)); + + #ifndef DOXYGEN_GENERATING_OUTPUT + template static std::unique_ptr instancer(const Arguments& arguments) { + return std::unique_ptr{new T{arguments}}; + } + #endif + + /** @copydoc Sdl2Application::Sdl2Application(const Arguments&, const Configuration&) */ + #ifdef DOXYGEN_GENERATING_OUTPUT + explicit AndroidApplication(const Arguments& arguments, const Configuration& configuration = Configuration()); + #else + /* To avoid "invalid use of incomplete type" */ + explicit AndroidApplication(const Arguments& arguments, const Configuration& configuration); + explicit AndroidApplication(const Arguments& arguments); + #endif + + /** @copydoc Sdl2Application::Sdl2Application(const Arguments&, std::nullptr_t) */ + explicit AndroidApplication(const Arguments& arguments, std::nullptr_t); + + /** @brief Copying is not allowed */ + AndroidApplication(const AndroidApplication&) = delete; + + /** @brief Moving is not allowed */ + AndroidApplication(AndroidApplication&&) = delete; + + virtual ~AndroidApplication(); + + /** @brief Copying is not allowed */ + AndroidApplication& operator=(const AndroidApplication&) = delete; + + /** @brief Moving is not allowed */ + AndroidApplication& operator=(AndroidApplication&&) = delete; + + protected: + /** @copydoc Sdl2Application::createContext() */ + #ifdef DOXYGEN_GENERATING_OUTPUT + void createContext(const Configuration& configuration = Configuration()); + #else + /* To avoid "invalid use of incomplete type" */ + void createContext(const Configuration& configuration); + void createContext(); + #endif + + /** @copydoc Sdl2Application::tryCreateContext() */ + bool tryCreateContext(const Configuration& configuration); + + /** @{ @name Screen handling */ + + /** @copydoc Sdl2Application::swapBuffers() */ + void swapBuffers(); + + /** @copydoc Sdl2Application::redraw() */ + void redraw() { _flags |= Flag::Redraw; } + + #ifdef DOXYGEN_GENERATING_OUTPUT + protected: + #else + private: + #endif + /** @copydoc Sdl2Application::viewportEvent() */ + virtual void viewportEvent(const Vector2i& size); + + /** @copydoc Sdl2Application::drawEvent() */ + virtual void drawEvent() = 0; + + /*@}*/ + + /** @{ @name Mouse handling */ + + #ifdef DOXYGEN_GENERATING_OUTPUT + protected: + #else + private: + #endif + /** + * @brief Mouse press event + * + * Called when mouse button is pressed. Default implementation does + * nothing. + */ + virtual void mousePressEvent(MouseEvent& event); + + /** + * @brief Mouse release event + * + * Called when mouse button is released. Default implementation does + * nothing. + */ + virtual void mouseReleaseEvent(MouseEvent& event); + + /** + * @brief Mouse move event + * + * Called when mouse is moved. Default implementation does nothing. + */ + virtual void mouseMoveEvent(MouseMoveEvent& event); + + /*@}*/ + + private: + struct LogOutput; + + enum class Flag: UnsignedByte { + Redraw = 1 << 0 + }; + typedef Containers::EnumSet Flags; + + static void commandEvent(android_app* state, std::int32_t cmd); + static std::int32_t inputEvent(android_app* state, AInputEvent* event); + + void initialize(); + + android_app* const _state; + Flags _flags; + + EGLDisplay _display; + EGLSurface _surface; + EGLContext _context; + + std::unique_ptr _c; + std::unique_ptr _logOutput; + + CORRADE_ENUMSET_FRIEND_OPERATORS(Flags) +}; + +CORRADE_ENUMSET_OPERATORS(AndroidApplication::Flags) + +/** +@brief %Configuration + +Double-buffered RGBA canvas with depth and stencil buffers. +@see @ref AndroidApplication(), @ref createContext(), @ref tryCreateContext() +*/ +class AndroidApplication::Configuration { + public: + constexpr /*implicit*/ Configuration() {} + + /** + * @brief Set window title + * @return Reference to self (for method chaining) + * + * @note This function does nothing and is included only for + * compatibility with other toolkits. You need to set the title + * separately in the `AndroidManifest.xml` file. + */ + template Configuration& setTitle(const T&) { return *this; } + + /** @brief Window size */ + Vector2i size() const { return _size; } + + /** + * @brief Set window size + * @return Reference to self (for method chaining) + * + * Default is `{0, 0}`, which means that the size of the physical + * window will be used. If set to different value than the physical + * size, the surface will be scaled. + */ + Configuration& setSize(const Vector2i& size) { + _size = size; + return *this; + } + + /** + * @brief Set context version + * + * @note This function does nothing and is included only for + * compatibility with other toolkits. @ref Version::GLES200 or + * @ref Version::GLES300 is used based on engine compile-time + * settings. + */ + Configuration& setVersion(Version) { return *this; } + + private: + Vector2i _size; +}; + +/** +@brief Base for input events + +@see @ref MouseEvent, @ref MouseMoveEvent, @ref mousePressEvent(), + @ref mouseReleaseEvent(), @ref mouseMoveEvent() +*/ +class AndroidApplication::InputEvent { + public: + /** @brief Copying is not allowed */ + InputEvent(const InputEvent&) = delete; + + /** @brief Moving is not allowed */ + InputEvent(InputEvent&&) = delete; + + /** @brief Copying is not allowed */ + InputEvent& operator=(const InputEvent&) = delete; + + /** @brief Moving is not allowed */ + InputEvent& operator=(InputEvent&&) = delete; + + /** + * @brief Set event as accepted + * + * If the event is ignored (i.e., not set as accepted), it will be + * propagated elsewhere, for example to the Android system or to + * another screen when using @ref BasicScreenedApplication "ScreenedApplication". + * By default is each event ignored and thus propagated. + */ + void setAccepted(bool accepted = true) { _accepted = accepted; } + + /** @brief Whether the event is accepted */ + bool isAccepted() const { return _accepted; } + + #ifndef DOXYGEN_GENERATING_OUTPUT + protected: + explicit InputEvent(AInputEvent* event): _event(event), _accepted(false) {} + + ~InputEvent() = default; + + AInputEvent* _event; + #endif + + private: + bool _accepted; +}; + +/** +@brief Mouse event + +@see @ref MouseMoveEvent, @ref mousePressEvent(), @ref mouseReleaseEvent() +*/ +class AndroidApplication::MouseEvent: public InputEvent { + friend class AndroidApplication; + + public: + /** + * @brief Mouse button + * + * @see @ref button() + */ + enum class Button: std::int32_t { + /** No button was pressed (touch or stylus event) */ + None = 0, + + /** + * Left mouse button. Note that this button is not set if only + * touch or stylus event occured. + * @attention Available since Android 4.0 (API level 14), not + * detectable in earlier versions. + */ + #if defined(DOXYGEN_GENERATING_OUTPUT) || __ANDROID_API__ >= 14 + Left = AMOTION_EVENT_BUTTON_PRIMARY, + #else + Left = 1 << 0, + #endif + + /** + * Middle mouse button or second stylus button + * @attention Available since Android 4.0 (API level 14), not + * detectable in earlier versions. + */ + #if defined(DOXYGEN_GENERATING_OUTPUT) || __ANDROID_API__ >= 14 + Middle = AMOTION_EVENT_BUTTON_TERTIARY, + #else + Middle = 1 << 1, + #endif + + /** + * Right mouse button or first stylus button + * @attention Available since Android 4.0 (API level 14), not + * detectable in earlier versions. + */ + #if defined(DOXYGEN_GENERATING_OUTPUT) || __ANDROID_API__ >= 14 + Right = AMOTION_EVENT_BUTTON_SECONDARY + #else + Right = 1 << 2 + #endif + }; + + /** @brief Button */ + Button button() { + #if __ANDROID_API__ >= 14 + return Button(AMotionEvent_getButtonState(_event)); + #else + return Button::None; + #endif + } + + /** @brief Position */ + Vector2i position() { + return {Int(AMotionEvent_getX(_event, 0)), + Int(AMotionEvent_getY(_event, 0))}; + } + + private: + MouseEvent(AInputEvent* event): InputEvent(event) {} +}; + +/** +@brief Mouse move event + +@see @ref MouseEvent, @ref mouseMoveEvent() +*/ +class AndroidApplication::MouseMoveEvent: public InputEvent { + friend class AndroidApplication; + + public: + /** + * @brief Mouse button + * + * @see @ref buttons() + */ + enum class Button: std::int32_t { + /** + * Left mouse button. Note that this button is not set if only + * touch or stylus event occured. + * @attention Available since Android 4.0 (API level 14), not + * detectable in earlier versions. + */ + #if defined(DOXYGEN_GENERATING_OUTPUT) || __ANDROID_API__ >= 14 + Left = AMOTION_EVENT_BUTTON_PRIMARY, + #else + Left = 1 << 0, + #endif + + /** + * Middle mouse button or second stylus button + * @attention Available since Android 4.0 (API level 14), not + * detectable in earlier versions. + */ + #if defined(DOXYGEN_GENERATING_OUTPUT) || __ANDROID_API__ >= 14 + Middle = AMOTION_EVENT_BUTTON_TERTIARY, + #else + Middle = 1 << 1, + #endif + + /** + * Right mouse button or first stylus button + * @attention Available since Android 4.0 (API level 14), not + * detectable in earlier versions. + */ + #if defined(DOXYGEN_GENERATING_OUTPUT) || __ANDROID_API__ >= 14 + Right = AMOTION_EVENT_BUTTON_SECONDARY + #else + Right = 1 << 2 + #endif + }; + + /** + * @brief Set of mouse buttons + * + * @see @ref buttons() + */ + typedef Containers::EnumSet Buttons; + + /** @brief Position */ + Vector2i position() const { + return {Int(AMotionEvent_getX(_event, 0)), + Int(AMotionEvent_getY(_event, 0))}; + } + + /** @brief Mouse buttons */ + Buttons buttons() const { + #if __ANDROID_API__ >= 14 + return Button(AMotionEvent_getButtonState(_event)); + #else + return {}; + #endif + } + + private: + MouseMoveEvent(AInputEvent* event): InputEvent(event) {} +}; + +CORRADE_ENUMSET_OPERATORS(AndroidApplication::MouseMoveEvent::Buttons) + +/** @hideinitializer +@brief Entry point for Android applications +@param className Class name + +See @ref Magnum::Platform::AndroidApplication "Platform::AndroidApplication" +for usage information. This macro abstracts out platform-specific entry point +code (the classic `main()` function cannot be used in Android). See +@ref portability-applications for more information. When no other application +header is included this macro is also aliased to `MAGNUM_APPLICATION_MAIN()`. +*/ +#define MAGNUM_ANDROIDAPPLICATION_MAIN(className) \ + void android_main(android_app* state) { \ + Magnum::Platform::AndroidApplication::exec(state, \ + Magnum::Platform::AndroidApplication::instancer); \ + } + +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_APPLICATION_MAIN +typedef AndroidApplication Application; +typedef BasicScreen Screen; +typedef BasicScreenedApplication ScreenedApplication; +#define MAGNUM_APPLICATION_MAIN(className) MAGNUM_ANDROIDAPPLICATION_MAIN(className) +#else +#undef MAGNUM_APPLICATION_MAIN +#endif +#endif + +}} + +#endif diff --git a/src/Magnum/Platform/CMakeLists.txt b/src/Magnum/Platform/CMakeLists.txt index 1afd6dc0e..e3a70508e 100644 --- a/src/Magnum/Platform/CMakeLists.txt +++ b/src/Magnum/Platform/CMakeLists.txt @@ -32,11 +32,32 @@ set(Platform_HEADERS install(FILES ${Platform_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) +# Android application +if(WITH_ANDROIDAPPLICATION) + if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL Android) + message(FATAL_ERROR "AndroidApplication is available only when targeting Android. Set WITH_ANDROIDAPPLICATION to OFF to skip building it.") + endif() + + include_directories(${ANDROID_NATIVE_APP_GLUE_INCLUDE_DIR}) + + add_library(MagnumAndroidApplication STATIC + AndroidApplication.cpp + Implementation/Egl.cpp + ${ANDROID_NATIVE_APP_GLUE_SRC}) + set_target_properties(MagnumAndroidApplication PROPERTIES DEBUG_POSTFIX "-d") + install(FILES AndroidApplication.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) + install(TARGETS MagnumAndroidApplication + RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} + LIBRARY DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR} + ARCHIVE DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) +endif() + # GLUT application if(WITH_GLUTAPPLICATION) find_package(GLUT) if(GLUT_FOUND) add_library(MagnumGlutApplication STATIC GlutApplication.cpp) + set_target_properties(MagnumGlutApplication PROPERTIES DEBUG_POSTFIX "-d") install(FILES GlutApplication.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) install(TARGETS MagnumGlutApplication RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} @@ -53,6 +74,7 @@ if(WITH_SDL2APPLICATION) if(SDL2_FOUND) include_directories(${SDL2_INCLUDE_DIR}) add_library(MagnumSdl2Application STATIC Sdl2Application.cpp) + set_target_properties(MagnumSdl2Application PROPERTIES DEBUG_POSTFIX "-d") install(FILES Sdl2Application.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) install(TARGETS MagnumSdl2Application RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} @@ -70,6 +92,7 @@ if(WITH_NACLAPPLICATION) endif() add_library(MagnumNaClApplication STATIC NaClApplication.cpp) + set_target_properties(MagnumNaClApplication PROPERTIES DEBUG_POSTFIX "-d") install(FILES NaClApplication.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) install(TARGETS MagnumNaClApplication RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} @@ -84,6 +107,7 @@ if(WITH_WINDOWLESSNACLAPPLICATION) endif() add_library(MagnumWindowlessNaClApplication STATIC WindowlessNaClApplication.cpp) + set_target_properties(MagnumWindowlessNaClApplication PROPERTIES DEBUG_POSTFIX "-d") install(FILES WindowlessNaClApplication.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) install(TARGETS MagnumWindowlessNaClApplication RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} @@ -109,6 +133,7 @@ if(WITH_GLXAPPLICATION) $ $ GlxApplication.cpp) + set_target_properties(MagnumGlxApplication PROPERTIES DEBUG_POSTFIX "-d") install(FILES GlxApplication.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) install(TARGETS MagnumGlxApplication RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} @@ -124,6 +149,7 @@ if(WITH_XEGLAPPLICATION) $ $ XEglApplication.cpp) + set_target_properties(MagnumXEglApplication PROPERTIES DEBUG_POSTFIX "-d") install(FILES XEglApplication.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) install(TARGETS MagnumXEglApplication RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} @@ -142,7 +168,9 @@ endif() if(WITH_WINDOWLESSGLXAPPLICATION) add_library(MagnumWindowlessGlxApplication STATIC WindowlessGlxApplication.cpp) # X11 macros are a mess, disable warnings for C-style casts - set_target_properties(MagnumWindowlessGlxApplication PROPERTIES COMPILE_FLAGS "-Wno-old-style-cast") + set_target_properties(MagnumWindowlessGlxApplication PROPERTIES + COMPILE_FLAGS "-Wno-old-style-cast" + DEBUG_POSTFIX "-d") install(FILES WindowlessGlxApplication.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform) install(TARGETS MagnumWindowlessGlxApplication RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} @@ -171,7 +199,9 @@ if(NEED_EGLCONTEXT) if(NOT EGL_FOUND) message(FATAL_ERROR "EGL library, required by some window contexts, was not found. Set WITH_*EGL*APPLICATION to OFF to skip building them.") endif() - add_library(MagnumEglContextHandler OBJECT Implementation/EglContextHandler.cpp) + add_library(MagnumEglContextHandler OBJECT + Implementation/EglContextHandler.cpp + Implementation/Egl.cpp) # X11 macros are a mess, disable warnings for C-style casts set_target_properties(MagnumEglContextHandler PROPERTIES COMPILE_FLAGS "-Wno-old-style-cast") endif() diff --git a/src/Magnum/Platform/GlutApplication.cpp b/src/Magnum/Platform/GlutApplication.cpp index acd2d80a4..21cd0e3f9 100644 --- a/src/Magnum/Platform/GlutApplication.cpp +++ b/src/Magnum/Platform/GlutApplication.cpp @@ -96,12 +96,18 @@ bool GlutApplication::tryCreateContext(const Configuration& configuration) { #endif } + /* Set context flags */ + glutInitContextFlags(int(configuration.flags())); + if(!glutCreateWindow(configuration.title().data())) { Error() << "Platform::GlutApplication::tryCreateContext(): cannot create context"; return false; } glutReshapeFunc(staticViewportEvent); - glutSpecialFunc(staticKeyEvent); + glutKeyboardFunc(staticKeyPressEvent); + glutKeyboardUpFunc(staticKeyReleaseEvent); + glutSpecialFunc(staticSpecialKeyPressEvent); + glutSpecialUpFunc(staticSpecialKeyReleaseEvent); glutMouseFunc(staticMouseEvent); glutMotionFunc(staticMouseMoveEvent); glutDisplayFunc(staticDrawEvent); @@ -114,11 +120,26 @@ GlutApplication::~GlutApplication() { delete c; } -void GlutApplication::staticKeyEvent(int key, int x, int y){ +void GlutApplication::staticKeyPressEvent(unsigned char key, int x, int y) { KeyEvent e(static_cast(key), {x, y}); instance->keyPressEvent(e); } +void GlutApplication::staticKeyReleaseEvent(unsigned char key, int x, int y) { + KeyEvent e(static_cast(key), {x, y}); + instance->keyReleaseEvent(e); +} + +void GlutApplication::staticSpecialKeyPressEvent(int key, int x, int y){ + KeyEvent e(static_cast(key << 16), {x, y}); + instance->keyPressEvent(e); +} + +void GlutApplication::staticSpecialKeyReleaseEvent(int key, int x, int y){ + KeyEvent e(static_cast(key << 16), {x, y}); + instance->keyReleaseEvent(e); +} + void GlutApplication::staticMouseEvent(int button, int state, int x, int y) { MouseEvent e(static_cast(button), {x, y}); if(state == GLUT_DOWN) diff --git a/src/Magnum/Platform/GlutApplication.h b/src/Magnum/Platform/GlutApplication.h index 1e33345fc..f130928ae 100644 --- a/src/Magnum/Platform/GlutApplication.h +++ b/src/Magnum/Platform/GlutApplication.h @@ -52,25 +52,41 @@ namespace Platform { /** @nosubgrouping @brief GLUT application -Application using GLUT toolkit. Supports keyboard handling for limited subset -of keys, mouse handling with support for changing cursor and mouse tracking and -warping. +Application using GLUT toolkit. Supports keyboard and mouse handling with +support for changing cursor and mouse tracking and warping. This application library is available only on desktop OpenGL (Linux, Windows, OS X). It depends on **GLUT** library and is built if `WITH_GLUTAPPLICATION` is -enabled in CMake. To use it, you need to request `%GlutApplication` component -in CMake, add `${MAGNUM_GLUTAPPLICATION_INCLUDE_DIRS}` to include path and link -to `${MAGNUM_GLUTAPPLICATION_LIBRARIES}`. If no other application is requested, +enabled in CMake. + +## Bootstrap application + +Fully contained base application using @ref GlutApplication along with +CMake setup is available in `base` branch of +[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) repository, +download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/base.tar.gz) +or [zip](https://github.com/mosra/magnum-bootstrap/archive/base.zip) file. +After extracting the downloaded archive you can build and run the application +with these four commands: + + mkdir build && cd build + cmake .. + cmake --build . + ./src/MyApplication # or ./src/Debug/MyApplication + +## General usage + +In CMake you need to request `%GlutApplication` component, add +`${MAGNUM_GLUTAPPLICATION_INCLUDE_DIRS}` to include path and link to +`${MAGNUM_GLUTAPPLICATION_LIBRARIES}`. If no other application is requested, you can also use generic `${MAGNUM_APPLICATION_INCLUDE_DIRS}` and `${MAGNUM_APPLICATION_LIBRARIES}` aliases to simplify porting. See @ref building and @ref cmake for more information. -@section GlutApplication-usage Usage - -You need to implement at least @ref drawEvent() to be able to draw on the -screen. The subclass can be then used directly in `main()` -- see convenience -macro @ref MAGNUM_GLUTAPPLICATION_MAIN(). See @ref platform for more -information. +In C++ code you need to implement at least @ref drawEvent() to be able to draw +on the screen. The subclass can be then used directly in `main()` -- see +convenience macro @ref MAGNUM_GLUTAPPLICATION_MAIN(). See @ref platform for +more information. @code class MyApplication: public Platform::GlutApplication { // implement required methods... @@ -124,7 +140,12 @@ class GlutApplication { /** @brief Moving is not allowed */ GlutApplication& operator=(GlutApplication&&) = delete; - /** @copydoc Sdl2Application::exec() */ + /** + * @brief Execute main loop + * @return Value for returning from `main()` + * + * See @ref MAGNUM_GLUTAPPLICATION_MAIN() for usage information. + */ int exec() { glutMainLoop(); return 0; @@ -173,12 +194,7 @@ class GlutApplication { /** @copydoc Sdl2Application::keyPressEvent() */ virtual void keyPressEvent(KeyEvent& event); - /** - * @brief Key release event - * - * Included only for compatibility with other toolkits, doesn't get - * called at all. - */ + /** @copydoc Sdl2Application::keyReleaseEvent() */ virtual void keyReleaseEvent(KeyEvent& event); /*@}*/ @@ -245,7 +261,11 @@ class GlutApplication { instance->viewportEvent({x, y}); } - static void staticKeyEvent(int key, int x, int y); + static void staticKeyPressEvent(unsigned char key, int x, int y); + static void staticKeyReleaseEvent(unsigned char key, int x, int y); + + static void staticSpecialKeyPressEvent(int key, int x, int y); + static void staticSpecialKeyReleaseEvent(int key, int x, int y); static void staticMouseEvent(int button, int state, int x, int y); @@ -268,6 +288,26 @@ Double-buffered RGBA window with depth and stencil buffers. */ class GlutApplication::Configuration { public: + /** + * @brief Context flag + * + * @see @ref Flags @ref setFlags() + */ + enum class Flag: int { + Debug = GLUT_DEBUG /**< Create debug context */ + }; + + /** + * @brief Context flags + * + * @see @ref setFlags() + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + typedef Containers::EnumSet Flags; + #else + typedef Containers::EnumSet Flags; + #endif + /*implicit*/ Configuration(); ~Configuration(); @@ -299,6 +339,20 @@ class GlutApplication::Configuration { return *this; } + /** @brief Context flags */ + Flags flags() const { return _flags; } + + /** + * @brief Set context flags + * @return Reference to self (for method chaining) + * + * Default is no flag. + */ + Configuration& setFlags(Flags flags) { + _flags = flags; + return *this; + } + /** @brief Context version */ Version version() const { return _version; } @@ -336,8 +390,11 @@ class GlutApplication::Configuration { Vector2i _size; Int _sampleCount; Version _version; + Flags _flags; }; +CORRADE_ENUMSET_OPERATORS(GlutApplication::Configuration::Flags) + /** @brief Base for input events @@ -395,27 +452,76 @@ class GlutApplication::KeyEvent: public GlutApplication::InputEvent { * * @see @ref key() */ - enum class Key: int { - Up = GLUT_KEY_UP, /**< Up arrow */ - Down = GLUT_KEY_DOWN, /**< Down arrow */ - Left = GLUT_KEY_LEFT, /**< Left arrow */ - Right = GLUT_KEY_RIGHT, /**< Right arrow */ - F1 = GLUT_KEY_F1, /**< F1 */ - F2 = GLUT_KEY_F2, /**< F2 */ - F3 = GLUT_KEY_F3, /**< F3 */ - F4 = GLUT_KEY_F4, /**< F4 */ - F5 = GLUT_KEY_F5, /**< F5 */ - F6 = GLUT_KEY_F6, /**< F6 */ - F7 = GLUT_KEY_F7, /**< F7 */ - F8 = GLUT_KEY_F8, /**< F8 */ - F9 = GLUT_KEY_F9, /**< F9 */ - F10 = GLUT_KEY_F10, /**< F10 */ - F11 = GLUT_KEY_F11, /**< F11 */ - F12 = GLUT_KEY_F12, /**< F12 */ - Home = GLUT_KEY_HOME, /**< Home */ - End = GLUT_KEY_END, /**< End */ - PageUp = GLUT_KEY_PAGE_UP, /**< Page up */ - PageDown = GLUT_KEY_PAGE_DOWN /**< Page down */ + enum class Key: UnsignedInt { + Esc = '\x1b', /**< Escape */ + + Up = GLUT_KEY_UP << 16, /**< Up arrow */ + Down = GLUT_KEY_DOWN << 16, /**< Down arrow */ + Left = GLUT_KEY_LEFT << 16, /**< Left arrow */ + Right = GLUT_KEY_RIGHT << 16, /**< Right arrow */ + F1 = GLUT_KEY_F1 << 16, /**< F1 */ + F2 = GLUT_KEY_F2 << 16, /**< F2 */ + F3 = GLUT_KEY_F3 << 16, /**< F3 */ + F4 = GLUT_KEY_F4 << 16, /**< F4 */ + F5 = GLUT_KEY_F5 << 16, /**< F5 */ + F6 = GLUT_KEY_F6 << 16, /**< F6 */ + F7 = GLUT_KEY_F7 << 16, /**< F7 */ + F8 = GLUT_KEY_F8 << 16, /**< F8 */ + F9 = GLUT_KEY_F9 << 16, /**< F9 */ + F10 = GLUT_KEY_F10 << 16, /**< F10 */ + F11 = GLUT_KEY_F11 << 16, /**< F11 */ + F12 = GLUT_KEY_F12 << 16, /**< F12 */ + Home = GLUT_KEY_HOME << 16, /**< Home */ + End = GLUT_KEY_END << 16, /**< End */ + PageUp = GLUT_KEY_PAGE_UP << 16, /**< Page up */ + PageDown = GLUT_KEY_PAGE_DOWN << 16, /**< Page down */ + + Space = ' ', /**< Space */ + Comma = ',', /**< Comma */ + Period = '.', /**< Period */ + Minus = '-', /**< Minus */ + Plus = '+', /**< Plus */ + Slash = '/', /**< Slash */ + Percent = '%', /**< Percent */ + Equal = '=', /**< Equal */ + + Zero = '0', /**< Zero */ + One = '1', /**< One */ + Two = '2', /**< Two */ + Three = '3', /**< Three */ + Four = '4', /**< Four */ + Five = '5', /**< Five */ + Six = '6', /**< Six */ + Seven = '7', /**< Seven */ + Eight = '8', /**< Eight */ + Nine = '9', /**< Nine */ + + A = 'a', /**< Letter A */ + B = 'b', /**< Letter B */ + C = 'c', /**< Letter C */ + D = 'd', /**< Letter D */ + E = 'e', /**< Letter E */ + F = 'f', /**< Letter F */ + G = 'g', /**< Letter G */ + H = 'h', /**< Letter H */ + I = 'i', /**< Letter I */ + J = 'j', /**< Letter J */ + K = 'k', /**< Letter K */ + L = 'l', /**< Letter L */ + M = 'm', /**< Letter M */ + N = 'n', /**< Letter N */ + O = 'o', /**< Letter O */ + P = 'p', /**< Letter P */ + Q = 'q', /**< Letter Q */ + R = 'r', /**< Letter R */ + S = 's', /**< Letter S */ + T = 't', /**< Letter T */ + U = 'u', /**< Letter U */ + V = 'v', /**< Letter V */ + W = 'w', /**< Letter W */ + X = 'x', /**< Letter X */ + Y = 'y', /**< Letter Y */ + Z = 'z' /**< Letter Z */ }; /** @brief Key */ @@ -512,9 +618,10 @@ class GlutApplication::MouseMoveEvent: public GlutApplication::InputEvent { @brief Entry point for GLUT-based applications @param className Class name -Can be with @ref Magnum::Platform::GlutApplication "Platform::GlutApplication" -subclasses used as equivalent to the following code to achieve better -portability, see @ref portability-applications for more information. +See @ref Magnum::Platform::GlutApplication "Platform::GlutApplication" for +usage information. This macro abstracts out platform-specific entry point code +and is equivalent to the following, see @ref portability-applications for more +information. @code int main(int argc, char** argv) { className app({argc, argv}); diff --git a/src/Magnum/Platform/GlxApplication.h b/src/Magnum/Platform/GlxApplication.h index 1c1ef02fa..773d8b937 100644 --- a/src/Magnum/Platform/GlxApplication.h +++ b/src/Magnum/Platform/GlxApplication.h @@ -42,18 +42,26 @@ Application using pure X11 and GLX. Supports keyboard and mouse handling. This application library is available on desktop OpenGL and @ref MAGNUM_TARGET_DESKTOP_GLES "OpenGL ES emulation on desktop" on Linux. It depends on **X11** library and is built if `WITH_GLXAPPLICATION` is enabled in -CMake. To use it, you need to request `%GlxApplication` component in CMake, add +CMake. + +## Bootstrap application + +The usage is very similar to @ref Sdl2Application, for which fully contained +base application along with CMake setup is available, see its documentation for +more information. + +## General usage + +In CMake you need to request `%GlxApplication` component, add `${MAGNUM_GLXAPPLICATION_INCLUDE_DIRS}` to include path and link to `${MAGNUM_GLXAPPLICATION_LIBRARIES}`. If no other application is requested, you can also use generic `${MAGNUM_APPLICATION_INCLUDE_DIRS}` and `${MAGNUM_APPLICATION_LIBRARIES}` aliases to simplify porting. See @ref building and @ref cmake for more information. -@section GlxApplication-usage Usage - -You need to implement at least @ref drawEvent() to be able to draw on the -screen. The subclass can be then used directly in `main()` -- see convenience -macro @ref MAGNUM_GLXAPPLICATION_MAIN(). See @ref platform for more +In C++ code you need to implement at least @ref drawEvent() to be able to draw +on the screen. The subclass can be then used directly in `main()` -- see +convenience macro @ref MAGNUM_GLXAPPLICATION_MAIN(). See @ref platform for more information. @code class MyApplication: public Platform::GlxApplication { @@ -88,9 +96,10 @@ class GlxApplication: public AbstractXApplication { @brief Entry point for GLX-based applications @param className Class name -Can be used with @ref Magnum::Platform::GlxApplication "Platform::GlxApplication" -subclasses as equivalent to the following code to achieve better portability, -see @ref portability-applications for more information. +See @ref Magnum::Platform::GlxApplication "Platform::GlxApplication" for usage +information. This macro abstracts out platform-specific entry point code and is +equivalent to the following, see @ref portability-applications for more +information. @code int main(int argc, char** argv) { className app({argc, argv}); diff --git a/src/Magnum/Platform/Implementation/Egl.cpp b/src/Magnum/Platform/Implementation/Egl.cpp new file mode 100644 index 000000000..35c5fdda1 --- /dev/null +++ b/src/Magnum/Platform/Implementation/Egl.cpp @@ -0,0 +1,54 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + 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 "Egl.h" + +namespace Magnum { namespace Platform { namespace Implementation { + +const char* eglErrorString(const EGLint error) { + switch(error) { + #define _error(name) case name: return #name; + _error(EGL_SUCCESS) + _error(EGL_NOT_INITIALIZED) + _error(EGL_BAD_ACCESS) + _error(EGL_BAD_ALLOC) + _error(EGL_BAD_ATTRIBUTE) + _error(EGL_BAD_CONTEXT) + _error(EGL_BAD_CONFIG) + _error(EGL_BAD_CURRENT_SURFACE) + _error(EGL_BAD_DISPLAY) + _error(EGL_BAD_SURFACE) + _error(EGL_BAD_MATCH) + _error(EGL_BAD_PARAMETER) + _error(EGL_BAD_NATIVE_PIXMAP) + _error(EGL_BAD_NATIVE_WINDOW) + _error(EGL_CONTEXT_LOST) + #undef _error + } + + return "EGL_(invalid)"; +} + +}}} diff --git a/src/Magnum/ImageFormat.h b/src/Magnum/Platform/Implementation/Egl.h similarity index 63% rename from src/Magnum/ImageFormat.h rename to src/Magnum/Platform/Implementation/Egl.h index d91f9280c..a84e4fe4c 100644 --- a/src/Magnum/ImageFormat.h +++ b/src/Magnum/Platform/Implementation/Egl.h @@ -1,5 +1,5 @@ -#ifndef Magnum_ImageFormat_h -#define Magnum_ImageFormat_h +#ifndef Magnum_Platform_Implementation_Egl_h +#define Magnum_Platform_Implementation_Egl_h /* This file is part of Magnum. @@ -25,33 +25,12 @@ DEALINGS IN THE SOFTWARE. */ -#ifdef MAGNUM_BUILD_DEPRECATED -/** @file - * @brief Enum @ref Magnum::ImageFormat, @ref Magnum::ImageType - * @deprecated Use @ref ColorFormat.h instead. - */ -#endif - -#include "Magnum/ColorFormat.h" - -#ifdef MAGNUM_BUILD_DEPRECATED -namespace Magnum { +#include -/** -@copybrief ColorFormat -@deprecated Use @ref Magnum::ColorFormat "ColorFormat" instead. -*/ -typedef CORRADE_DEPRECATED("use ColorFormat instead") ColorFormat ImageFormat; +namespace Magnum { namespace Platform { namespace Implementation { -/** -@copybrief ColorType -@deprecated Use @ref Magnum::ColorType "ColorType" instead. -*/ -typedef CORRADE_DEPRECATED("use ColorType instead") ColorType ImageType; +const char* eglErrorString(EGLint error); -} -#else -#error this header is available only on deprecated build -#endif +}}} #endif diff --git a/src/Magnum/Platform/Implementation/EglContextHandler.cpp b/src/Magnum/Platform/Implementation/EglContextHandler.cpp index 7b3419a74..153dd7bad 100644 --- a/src/Magnum/Platform/Implementation/EglContextHandler.cpp +++ b/src/Magnum/Platform/Implementation/EglContextHandler.cpp @@ -32,6 +32,8 @@ #include "Magnum/Context.h" #include "Magnum/Version.h" +#include "Egl.h" + namespace Magnum { namespace Platform { namespace Implementation { EglContextHandler::~EglContextHandler() { @@ -44,7 +46,7 @@ VisualId EglContextHandler::getVisualId(EGLNativeDisplayType nativeDisplay) { /* Initialize */ display = eglGetDisplay(nativeDisplay); if(!eglInitialize(display, nullptr, nullptr)) { - Error() << "Cannot initialize EGL:" << errorString(eglGetError()); + Error() << "Cannot initialize EGL:" << Implementation::eglErrorString(eglGetError()); std::exit(1); } @@ -54,7 +56,7 @@ VisualId EglContextHandler::getVisualId(EGLNativeDisplayType nativeDisplay) { EGLenum api = EGL_OPENGL_ES_API; #endif if(!eglBindAPI(api)) { - Error() << "Cannot bind EGL API:" << errorString(eglGetError()); + Error() << "Cannot bind EGL API:" << Implementation::eglErrorString(eglGetError()); std::exit(1); } @@ -67,7 +69,7 @@ VisualId EglContextHandler::getVisualId(EGLNativeDisplayType nativeDisplay) { #ifndef MAGNUM_TARGET_GLES EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, #elif defined(MAGNUM_TARGET_GLES3) - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR, #elif defined(MAGNUM_TARGET_GLES2) EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, #else @@ -77,7 +79,7 @@ VisualId EglContextHandler::getVisualId(EGLNativeDisplayType nativeDisplay) { }; EGLint configCount; if(!eglChooseConfig(display, attribs, &config, 1, &configCount)) { - Error() << "Cannot get EGL visual config:" << errorString(eglGetError()); + Error() << "Cannot get EGL visual config:" << Implementation::eglErrorString(eglGetError()); std::exit(1); } @@ -89,7 +91,7 @@ VisualId EglContextHandler::getVisualId(EGLNativeDisplayType nativeDisplay) { /* Get visual ID */ EGLint visualId; if(!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &visualId)) { - Error() << "Cannot get native visual ID:" << errorString(eglGetError()); + Error() << "Cannot get native visual ID:" << Implementation::eglErrorString(eglGetError()); std::exit(1); } @@ -140,40 +142,16 @@ void EglContextHandler::createContext(const AbstractXApplication::Configuration& } #endif - if(!eglCreateContext(display, config, EGL_NO_CONTEXT, attributes)) { - Error() << "Cannot create EGL context:" << errorString(eglGetError()); + if(!(context = eglCreateContext(display, config, EGL_NO_CONTEXT, attributes))) { + Error() << "Cannot create EGL context:" << Implementation::eglErrorString(eglGetError()); std::exit(1); } if(!(surface = eglCreateWindowSurface(display, config, window, nullptr))) { - Error() << "Cannot create window surface:" << errorString(eglGetError()); + Error() << "Cannot create window surface:" << Implementation::eglErrorString(eglGetError()); std::exit(1); } /** @bug Fixme: On desktop OpenGL and Mesa EGL implementation OpenGL version is 1.0, which is wrong */ } -const char* EglContextHandler::errorString(EGLint error) { - switch(error) { - #define _error(name) case name: return #name; - _error(EGL_SUCCESS) - _error(EGL_NOT_INITIALIZED) - _error(EGL_BAD_ACCESS) - _error(EGL_BAD_ALLOC) - _error(EGL_BAD_ATTRIBUTE) - _error(EGL_BAD_CONTEXT) - _error(EGL_BAD_CONFIG) - _error(EGL_BAD_CURRENT_SURFACE) - _error(EGL_BAD_DISPLAY) - _error(EGL_BAD_SURFACE) - _error(EGL_BAD_MATCH) - _error(EGL_BAD_PARAMETER) - _error(EGL_BAD_NATIVE_PIXMAP) - _error(EGL_BAD_NATIVE_WINDOW) - _error(EGL_CONTEXT_LOST) - #undef _error - } - - return {}; -} - }}} diff --git a/src/Magnum/Platform/Implementation/EglContextHandler.h b/src/Magnum/Platform/Implementation/EglContextHandler.h index 2a4ff5ae5..3f1dc51d3 100644 --- a/src/Magnum/Platform/Implementation/EglContextHandler.h +++ b/src/Magnum/Platform/Implementation/EglContextHandler.h @@ -31,6 +31,7 @@ #include /* undef Xlib nonsense to avoid conflicts */ #undef None +#undef Complex #include @@ -71,8 +72,6 @@ class EglContextHandler: public AbstractContextHandler @@ -135,9 +189,9 @@ If you target @ref CORRADE_TARGET_NACL_GLIBC "glibc", you need to specify also all additional dependencies. See [Native Client](https://developers.google.com/native-client/) documentation for more information. -@section NaClApplication-console Redirecting output to Chrome's JavaScript console +## Redirecting output to Chrome's JavaScript console -The application redirects @ref Corrade::Utility::Debug "Debug", +The application by default redirects @ref Corrade::Utility::Debug "Debug", @ref Corrade::Utility::Warning "Warning" and @ref Corrade::Utility::Error "Error" output to JavaScript console. See also @ref Corrade::Utility::NaClConsoleStreamBuffer for more information. @@ -347,7 +401,7 @@ class NaClApplication: public pp::Instance, public pp::Graphics3DClient, public @brief %Configuration Double-buffered RGBA canvas with depth and stencil buffers. -@see @ref NaClApplication(), @ref createContext() +@see @ref NaClApplication(), @ref createContext(), @ref tryCreateContext() */ class NaClApplication::Configuration { public: @@ -428,33 +482,6 @@ class NaClApplication::InputEvent { Ctrl = PP_INPUTEVENT_MODIFIER_CONTROLKEY, /**< Ctrl */ Alt = PP_INPUTEVENT_MODIFIER_ALTKEY, /**< Alt */ Meta = PP_INPUTEVENT_MODIFIER_METAKEY, /**< Meta */ - - #ifdef MAGNUM_BUILD_DEPRECATED - /** - * @copybrief Button::Left - * @deprecated Use @ref Magnum::Platform::NaClApplication::InputEvent::buttons() "buttons()" - * and @ref Magnum::Platform::NaClApplication::InputEvent::Button::Left "Button::Left" - * instead. - */ - LeftButton = PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN, - - /** - * @copybrief Button::Middle - * @deprecated Use @ref Magnum::Platform::NaClApplication::InputEvent::buttons() "buttons()" - * and @ref Magnum::Platform::NaClApplication::InputEvent::Button::Middle "Button::Middle" - * instead. - */ - MiddleButton = PP_INPUTEVENT_MODIFIER_MIDDLEBUTTONDOWN, - - /** - * @copybrief Button::Right - * @deprecated Use @ref Magnum::Platform::NaClApplication::InputEvent::buttons() "buttons()" - * and @ref Magnum::Platform::NaClApplication::InputEvent::Button::Right "Button::Right" - * instead. - */ - RightButton = PP_INPUTEVENT_MODIFIER_RIGHTBUTTONDOWN, - #endif - CapsLock = PP_INPUTEVENT_MODIFIER_CAPSLOCKKEY, /**< Caps lock */ NumLock = PP_INPUTEVENT_MODIFIER_NUMLOCKKEY /**< Num lock */ }; @@ -718,7 +745,9 @@ namespace Implementation { @brief Entry point for NaCl application @param application Application class name -See @ref Magnum::Platform::NaClApplication "Platform::NaClApplication" and +See @ref Magnum::Platform::NaClApplication "Platform::NaClApplication" for +usage information. This macro abstracts out platform-specific entry point code +(the classic `main()` function cannot be used in NaCl). See @ref portability-applications for more information. When no other application header is included this macro is also aliased to `MAGNUM_APPLICATION_MAIN()`. */ diff --git a/src/Magnum/Platform/Sdl2Application.cpp b/src/Magnum/Platform/Sdl2Application.cpp index 507c6259d..c4fbe5950 100644 --- a/src/Magnum/Platform/Sdl2Application.cpp +++ b/src/Magnum/Platform/Sdl2Application.cpp @@ -115,6 +115,11 @@ bool Sdl2Application::tryCreateContext(const Configuration& configuration) { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, configuration.sampleCount() > 1 ? 1 : 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, configuration.sampleCount()); + #ifndef CORRADE_TARGET_EMSCRIPTEN + /* Context flags */ + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, int(configuration.flags())); + #endif + /* Flags: if not hidden, set as shown */ Uint32 windowFlags(configuration.windowFlags()); if(!(configuration.windowFlags() & Configuration::WindowFlag::Hidden)) windowFlags |= SDL_WINDOW_SHOWN; diff --git a/src/Magnum/Platform/Sdl2Application.h b/src/Magnum/Platform/Sdl2Application.h index ad1d4a5f1..da83b8bcc 100644 --- a/src/Magnum/Platform/Sdl2Application.h +++ b/src/Magnum/Platform/Sdl2Application.h @@ -46,10 +46,6 @@ #include "Magnum/Version.h" #endif -#ifdef MAGNUM_BUILD_DEPRECATED -#include -#endif - namespace Magnum { class Context; @@ -63,21 +59,70 @@ Application using [Simple DirectMedia Layer](http://www.libsdl.org/) toolkit. Supports keyboard and mouse handling. This application library is in theory available for all platforms for which -SDL2 is ported (thus also @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten", but not -@ref CORRADE_TARGET_NACL "NaCl"). It depends on **SDL2** library (Emscripten -has it built in) and is built if `WITH_SDL2APPLICATION` is enabled in CMake. To -use it, you need to copy `FindSDL2.cmake` from `modules/` directory in %Magnum -source to `modules/` dir in your project (so CMake is able to find SDL2), -request `%Sdl2Application` component in CMake, add -`${MAGNUM_SDL2APPLICATION_INCLUDE_DIRS}` to include path and link to -`${MAGNUM_SDL2APPLICATION_LIBRARIES}`. If no other application is requested, -you can also use generic `${MAGNUM_APPLICATION_INCLUDE_DIRS}` and -`${MAGNUM_APPLICATION_LIBRARIES}` aliases to simplify porting. See +SDL2 is ported (thus also @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten", see +respective sections in @ref building-corrade-cross-emscripten "Corrade's" and +@ref building-cross-emscripten "Magnum's" building documentation). It depends +on **SDL2** library (Emscripten has it built in) and is built if +`WITH_SDL2APPLICATION` is enabled in CMake. + +## Bootstrap application + +Fully contained base application using @ref Sdl2Application along with +CMake setup is available in `base-sdl2` branch of +[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) repository, +download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/base-sdl2.tar.gz) +or [zip](https://github.com/mosra/magnum-bootstrap/archive/base-sdl2.zip) file. +After extracting the downloaded archive you can build and run the application +with these four commands: + + mkdir build && cd build + cmake .. + cmake --build . + ./src/MyApplication # or ./src/Debug/MyApplication + +## Bootstrap application for Emscripten + +Fully contained base application using @ref Sdl2Application for both desktop +and Emscripten build along with full HTML markup and CMake setup is available +in `base-emscripten` branch of [Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) +repository, download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/base-emscripten.tar.gz) +or [zip](https://github.com/mosra/magnum-bootstrap/archive/base-emscripten.zip) +file. After extracting the downloaded archive, you can do the desktop build in +the same way as above. For the Emscripten build you also need to put the +contents of toolchains repository from https://github.com/mosra/toolchains +in `toolchains/` subdirectory. Don't forget to adapt `EMSCRIPTEN_PREFIX` +variable in `toolchains/generic/Emscripten.cmake` to path where Emscripten is +installed. Default is `/usr/emscripten`. + +Then create build directory and run `cmake` and build/install commands in it. +The toolchain needs access to its platform file, so be sure to properly set +**absolute** path to `toolchains/modules/` directory containing `Platform/Emscripten.cmake`. +Set `CMAKE_INSTALL_PREFIX` to have the files installed in proper location (a +webserver, e.g. `/srv/http/emscripten`). + + mkdir build-emscripten && cd build-emscripten + cmake .. \ + -DCMAKE_MODULE_PATH="/absolute/path/to/toolchains/modules" \ + -DCMAKE_TOOLCHAIN_FILE="../toolchains/generic/Emscripten.cmake" + -DCMAKE_INSTALL_PREFIX=/srv/http/emscripten + cmake --build . + cmake --build . --target install + +You can then open `MyApplication.html` in Chrome or Firefox (through webserver, +e.g. `http://localhost/emscripten/MyApplication.html`). + +## General usage + +For CMake you need to copy `FindSDL2.cmake` from `modules/` directory in +%Magnum source to `modules/` dir in your project (so it is able to find SDL2). +In case of Emscripten you need also `FindOpenGLES2.cmake`. Request +`%Sdl2Application` component, add `${MAGNUM_SDL2APPLICATION_INCLUDE_DIRS}` +to include path and link to `${MAGNUM_SDL2APPLICATION_LIBRARIES}`. If no other +application is requested, you can also use generic `${MAGNUM_APPLICATION_INCLUDE_DIRS}` +and `${MAGNUM_APPLICATION_LIBRARIES}` aliases to simplify porting. See @ref building and @ref cmake for more information. -@section Sdl2Application-usage Usage - -You need to implement at least @ref drawEvent() to be able to draw on the +In C++ code you need to implement at least @ref drawEvent() to be able to draw on the screen. The subclass can be then used directly in `main()` -- see convenience macro @ref MAGNUM_SDL2APPLICATION_MAIN(). See @ref platform for more information. @@ -92,14 +137,15 @@ If no other application header is included, this class is also aliased to `Platform::Application` and the macro is aliased to `MAGNUM_APPLICATION_MAIN()` to simplify porting. -@section Sdl2Application-html Usage with Emscripten +### Usage with Emscripten -If you are targetting @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten", you need to -provide HTML markup for your application. Template one is below, you can modify -it to your liking. The markup references two files, `EmscriptenApplication.js` -and `WebApplication.css`, both are in `Platform/` directory in the source tree -and are also installed into `share/magnum/` inside your Emscripten toolchain. -Change `<application>` to name of your executable. +If you are targetting Emscripten, you need to provide HTML markup for your +application. Template one is below or in the bootstrap application, you can +modify it to your liking. The markup references two files, +`EmscriptenApplication.js` and `WebApplication.css`, both are in `Platform/` +directory in the source tree and are also installed into `share/magnum/` inside +your Emscripten toolchain. Change `<application>` to name of your +executable. @code @@ -127,9 +173,11 @@ file contains event listeners which print loading status on the page. The status displayed in the remaining two `<div>`s, if they are available. The CSS file contains rudimentary style to avoid eye bleeding. -The application redirects @ref Corrade::Utility::Debug "Debug", -@ref Corrade::Utility::Warning "Warning" and @ref Corrade::Utility::Error "Error" -output to JavaScript console. +## Redirecting output to JavaScript console + +The application redirects all output (thus also @ref Corrade::Utility::Debug "Debug", +@ref Corrade::Utility::Warning "Warning" and @ref Corrade::Utility::Error "Error") +to JavaScript console. */ class Sdl2Application { public: @@ -189,7 +237,9 @@ class Sdl2Application { /** * @brief Execute main loop - * @return Value for returning from `main()`. + * @return Value for returning from `main()` + * + * See @ref MAGNUM_SDL2APPLICATION_MAIN() for usage information. */ int exec(); @@ -237,7 +287,8 @@ class Sdl2Application { * @brief Redraw immediately * * Marks the window for redrawing, resulting in call to @ref drawEvent() - * in the next iteration. + * in the next iteration. You can call it from @ref drawEvent() itself + * to redraw immediately without waiting for user input. */ void redraw() { flags |= Flag::Redraw; } @@ -380,6 +431,35 @@ depth buffer. */ class Sdl2Application::Configuration { public: + #ifndef CORRADE_TARGET_EMSCRIPTEN + /** + * @brief Context flag + * + * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + * @see @ref Flags @ref setFlags() + * @todo re-enable when Emscripten has proper SDL2 support + */ + enum class Flag: int { + Debug = SDL_GL_CONTEXT_DEBUG_FLAG, /**< Create debug context */ + + /** Create context with robust access */ + RobustAccess = SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG + }; + + /** + * @brief Context flags + * + * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + * @see @ref setFlags() + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + typedef Containers::EnumSet Flags; + #else + typedef Containers::EnumSet Flags; + #endif + #endif + /** * @brief Window flag * @@ -394,14 +474,6 @@ class Sdl2Application::Configuration { MouseLocked = SDL_WINDOW_INPUT_GRABBED /**< Window with mouse locked */ }; - #ifdef MAGNUM_BUILD_DEPRECATED - /** - * @copybrief WindowFlag - * @deprecated Use @ref Magnum::Platform::Sdl2Application::Configuration::WindowFlag "WindowFlag" instead. - */ - typedef CORRADE_DEPRECATED("use WindowFlag instead") WindowFlag Flag; - #endif - /** * @brief Window flags * @@ -415,14 +487,6 @@ class Sdl2Application::Configuration { typedef Containers::EnumSet WindowFlags; #endif - #ifdef MAGNUM_BUILD_DEPRECATED - /** - * @copybrief WindowFlags - * @deprecated Use @ref Magnum::Platform::Sdl2Application::Configuration::WindowFlags "WindowFlags" instead. - */ - typedef CORRADE_DEPRECATED("use WindowFlags instead") WindowFlags Flags; - #endif - /*implicit*/ Configuration(); ~Configuration(); @@ -470,16 +534,6 @@ class Sdl2Application::Configuration { /** @brief Window flags */ WindowFlags windowFlags() const { return _windowFlags; } - #ifdef MAGNUM_BUILD_DEPRECATED - /** - * @copybrief windowFlags() - * @deprecated Use @ref Magnum::Platform::Sdl2Application::Configuration::windowFlags() "windowFlags()" instead. - */ - CORRADE_DEPRECATED("use windowFlags() instead") WindowFlags flags() const { - return windowFlags(); - } - #endif - /** * @brief Set window flags * @return Reference to self (for method chaining) @@ -491,17 +545,26 @@ class Sdl2Application::Configuration { return *this; } - #ifdef MAGNUM_BUILD_DEPRECATED + #ifndef CORRADE_TARGET_EMSCRIPTEN + /** + * @brief Context flags + * + * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". + */ + Flags flags() const { return _flags; } + /** - * @copybrief setWindowFlags() - * @deprecated Use @ref Magnum::Platform::Sdl2Application::Configuration::setWindowFlags "setWindowFlags()" instead. + * @brief Set context flags + * @return Reference to self (for method chaining) + * + * Default is no flag. + * @note Not available in @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten". */ - CORRADE_DEPRECATED("use setWindowFlags() instead") Configuration& setFlags(WindowFlags flags) { - return setWindowFlags(flags); + Configuration& setFlags(Flags flags) { + _flags = flags; + return *this; } - #endif - #ifndef CORRADE_TARGET_EMSCRIPTEN /** * @brief Context version * @@ -553,9 +616,13 @@ class Sdl2Application::Configuration { Int _sampleCount; #ifndef CORRADE_TARGET_EMSCRIPTEN Version _version; + Flags _flags; #endif }; +#ifndef CORRADE_TARGET_EMSCRIPTEN +CORRADE_ENUMSET_OPERATORS(Sdl2Application::Configuration::Flags) +#endif CORRADE_ENUMSET_OPERATORS(Sdl2Application::Configuration::WindowFlags) /** @@ -841,9 +908,10 @@ class Sdl2Application::MouseMoveEvent: public Sdl2Application::InputEvent { @brief Entry point for SDL2-based applications @param className Class name -Can be used with @ref Magnum::Platform::Sdl2Application "Platform::Sdl2Application" -subclasses as equivalent to the following code to achieve better portability, -see @ref portability-applications for more information. +See @ref Magnum::Platform::Sdl2Application "Platform::Sdl2Application" for +usage information. This macro abstracts out platform-specific entry point code +and is equivalent to the following, see @ref portability-applications for more +information. @code int main(int argc, char** argv) { className app({argc, argv}); diff --git a/src/Magnum/Platform/WindowlessGlxApplication.h b/src/Magnum/Platform/WindowlessGlxApplication.h index 9b418475a..eef2649db 100644 --- a/src/Magnum/Platform/WindowlessGlxApplication.h +++ b/src/Magnum/Platform/WindowlessGlxApplication.h @@ -53,16 +53,33 @@ Application for offscreen rendering using pure X11 and GLX. This application library is available on desktop OpenGL and @ref MAGNUM_TARGET_DESKTOP_GLES "OpenGL ES emulation on desktop" on Linux. It depends on **X11** library and is built if `WITH_WINDOWLESSGLXAPPLICATION` is -enabled in CMake. To use it, you need to request `%WindowlessGlxApplication` -component in CMake, add `${MAGNUM_WINDOWLESSGLXAPPLICATION_INCLUDE_DIRS}` to -include path and link to `${MAGNUM_WINDOWLESSGLXAPPLICATION_LIBRARIES}`. If no -other windowless application is requested, you can also use generic +enabled in CMake. + +## Bootstrap application + +Fully contained windowless application using @ref WindowlessGlxApplication +along with CMake setup is available in `base-windowless` branch of +[Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap) +repository, download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/base-windowless.tar.gz) +or [zip](https://github.com/mosra/magnum-bootstrap/archive/base-windowless.zip) +file. After extracting the downloaded archive you can build and run the +application with these four commands: + + mkdir build && cd build + cmake .. + cmake --build . + ./src/MyApplication # or ./src/Debug/MyApplication + +## General usage + +In CMake you need to request `%WindowlessGlxApplication` component, add +`${MAGNUM_WINDOWLESSGLXAPPLICATION_INCLUDE_DIRS}` to include path and link to +`${MAGNUM_WINDOWLESSGLXAPPLICATION_LIBRARIES}`. If no other windowless +application is requested, you can also use generic `${MAGNUM_WINDOWLESSAPPLICATION_INCLUDE_DIRS}` and `${MAGNUM_WINDOWLESSAPPLICATION_LIBRARIES}` aliases to simplify porting. See @ref building and @ref cmake for more information. -@section WindowlessGlxApplication-usage Usage - Place your code into @ref exec(). The subclass can be then used directly in `main()` -- see convenience macro @ref MAGNUM_WINDOWLESSGLXAPPLICATION_MAIN(). See @ref platform for more information. @@ -117,7 +134,10 @@ class WindowlessGlxApplication { /** * @brief Execute application - * @return Value for returning from `main()`. + * @return Value for returning from `main()` + * + * See @ref MAGNUM_WINDOWLESSGLXAPPLICATION_MAIN() for usage + * information. */ virtual int exec() = 0; @@ -161,9 +181,10 @@ class WindowlessGlxApplication::Configuration { @brief Entry point for windowless GLX application @param className Class name -Can be used with @ref Magnum::Platform::WindowlessGlxApplication "Platform::WindowlessGlxApplication" -subclasses as equivalent to the following code to achieve better portability, -see @ref portability-applications for more information. +See @ref Magnum::Platform::WindowlessGlxApplication "Platform::WindowlessGlxApplication" +for usage information. This macro abstracts out platform-specific entry point +code and is equivalent to the following, see @ref portability-applications for +more information. @code int main(int argc, char** argv) { className app({argc, argv}); diff --git a/src/Magnum/Platform/WindowlessNaClApplication.h b/src/Magnum/Platform/WindowlessNaClApplication.h index 0aa0a1860..503650f6a 100644 --- a/src/Magnum/Platform/WindowlessNaClApplication.h +++ b/src/Magnum/Platform/WindowlessNaClApplication.h @@ -52,9 +52,20 @@ namespace Magnum { namespace Platform { Application for offscreen rendering running in [Google Chrome Native Client](https://developers.google.com/native-client/). -This application library is available only in @ref CORRADE_TARGET_NACL "Native Client". -It is built if `WITH_WINDOWLESSNACLAPPLICATION` is enabled in CMake. To use it, -you need to request `%WindowlessNaClApplication` component in CMake, add +This application library is available only in @ref CORRADE_TARGET_NACL "Native Client", +see respective sections in @ref building-corrade-cross-nacl "Corrade's" and +@ref building-cross-nacl "Magnum's" building documentation. It is built if +`WITH_WINDOWLESSNACLAPPLICATION` is enabled in CMake. + +## Bootstrap application + +The usage is very similar to @ref NaClApplication, for which fully contained +base application along with CMake setup is available, see its documentation for +more information. + +## General Usage + +In CMake you need to request `%WindowlessNaClApplication` component, add `${MAGNUM_WINDOWLESSNACLAPPLICATION_INCLUDE_DIRS}` to include path and link to `${MAGNUM_WINDOWLESSNACLAPPLICATION_LIBRARIES}`. If no other windowless application is requested, you can also use generic @@ -62,8 +73,6 @@ application is requested, you can also use generic `${MAGNUM_WINDOWLESSAPPLICATION_LIBRARIES}` aliases to simplify porting. See @ref building and @ref cmake for more information. -@section WindowlessNaClApplication-usage Usage - Place your code into @ref exec(). The subclass must be then registered to NaCl API using @ref MAGNUM_WINDOWLESSNACLAPPLICATION_MAIN() macro. See @ref platform for more information. @@ -78,7 +87,7 @@ If no other application header is included, this class is also aliased to `Platform::WindowlessApplication` and the macro is aliased to `MAGNUM_WINDOWLESSAPPLICATION_MAIN()` to simplify porting. -@section WindowlessNaClApplication-html HTML markup and NMF file +### HTML markup and NMF file You need to provide HTML markup containing `<embed>` pointing to `*.nmf` file describing the application. See @ref NaClApplication for more information. @@ -86,9 +95,9 @@ You may want to hide the `<embed>` (for example using CSS `visibility: hidden;`), as it probably won't display anything to default framebuffer. -@section WindowlessNaClApplication-console Redirecting output to Chrome's JavaScript console +## Redirecting output to Chrome's JavaScript console -The application redirects @ref Corrade::Utility::Debug "Debug", +The application by default redirects @ref Corrade::Utility::Debug "Debug", @ref Corrade::Utility::Warning "Warning" and @ref Corrade::Utility::Error "Error" output to JavaScript console. See also @ref Corrade::Utility::NaClConsoleStreamBuffer for more information. @@ -128,9 +137,17 @@ class WindowlessNaClApplication: public pp::Instance, public pp::Graphics3DClien /** @brief Moving is not allowed */ WindowlessNaClApplication& operator=(WindowlessNaClApplication&&) = delete; + #ifdef DOXYGEN_GENERATING_OUTPUT + protected: + #else + private: + #endif /** * @brief Execute application - * @return Value for returning from `main()`. + * @return Value for returning from `main()` + * + * This function is not meant to be called from user code, see + * @ref MAGNUM_WINDOWLESSNACLAPPLICATION_MAIN() for usage information. */ virtual int exec() = 0; @@ -194,8 +211,10 @@ namespace Implementation { @param application Application class name See @ref Magnum::Platform::WindowlessNaClApplication "Platform::WindowlessNaClApplication" -and @ref portability-applications for more information. When no other -windowless application header is included this macro is also aliased to +for usage information. This macro abstracts out platform-specific entry point +code (the classic `main()` function cannot be used in NaCl). See +@ref portability-applications for more information. When no other windowless +application header is included this macro is also aliased to `MAGNUM_WINDOWLESSAPPLICATION_MAIN()`. */ /* look at that insane placement of __attribute__. WTF. */ diff --git a/src/Magnum/Platform/XEglApplication.h b/src/Magnum/Platform/XEglApplication.h index 405d6da32..0d9e6cd2b 100644 --- a/src/Magnum/Platform/XEglApplication.h +++ b/src/Magnum/Platform/XEglApplication.h @@ -41,21 +41,28 @@ Application using pure X11 and EGL. Supports keyboard and mouse handling. This application library is available on both desktop OpenGL and @ref MAGNUM_TARGET_GLES "OpenGL ES" on Linux. It depends on **X11** and **EGL** -libraries and is built if `WITH_XEGLAPPLICATION` is enabled in CMake. To use -it, you need to copy `FindEGL.cmake` from `modules/` directory in %Magnum -source to `modules/` dir in your project (so CMake is able to find EGL), -request `%XEglApplication` component in CMake, add `${MAGNUM_XEGLAPPLICATION_INCLUDE_DIRS}` -to include path and link to `${MAGNUM_XEGLAPPLICATION_LIBRARIES}`. If no other +libraries and is built if `WITH_XEGLAPPLICATION` is enabled in CMake. + +## Bootstrap application + +The usage is very similar to @ref Sdl2Application, for which fully contained +base application along with CMake setup is available, see its documentation for +more information. + +## General usage + +For CMake you need to copy `FindEGL.cmake` from `modules/` directory in %Magnum +source to `modules/` dir in your project (so it is able to find EGL), request +`%XEglApplication` component, add `${MAGNUM_XEGLAPPLICATION_INCLUDE_DIRS}` to +include path and link to `${MAGNUM_XEGLAPPLICATION_LIBRARIES}`. If no other application is requested, you can also use generic `${MAGNUM_APPLICATION_INCLUDE_DIRS}` and `${MAGNUM_APPLICATION_LIBRARIES}` aliases to simplify porting. See @ref building and @ref cmake for more information. -@section XEglApplication-usage Usage - -You need to implement at least @ref drawEvent() to be able to draw on the -screen. The subclass can be then used directly in `main()` -- see convenience -macro @ref MAGNUM_XEGLAPPLICATION_MAIN(). See @ref platform for more -information. +In C++ code you need to implement at least @ref drawEvent() to be able to draw +on the screen. The subclass can be then used directly in `main()` -- see +convenience macro @ref MAGNUM_XEGLAPPLICATION_MAIN(). See @ref platform for +more information. @code class MyApplication: public Platform::XEglApplication { // implement required methods... @@ -89,9 +96,10 @@ class XEglApplication: public AbstractXApplication { @brief Entry point for X/EGL-based applications @param className Class name -Can be used with @ref Magnum::Platform::XEglApplication "Platform::XEglApplication" -subclasses as equivalent to the following code to achieve better portability, -see @ref portability-applications for more information. +See @ref Magnum::Platform::XEglApplication "Platform::XEglApplication" for +usage information. This macro abstracts out platform-specific entry point code +and is equivalent to the following, see @ref portability-applications for more +information. @code int main(int argc, char** argv) { className app({argc, argv}); diff --git a/src/Magnum/Platform/magnum-info.cpp b/src/Magnum/Platform/magnum-info.cpp index 5e83b4762..ab4814a31 100644 --- a/src/Magnum/Platform/magnum-info.cpp +++ b/src/Magnum/Platform/magnum-info.cpp @@ -61,8 +61,8 @@ MagnumInfo::MagnumInfo(const Arguments& arguments): Platform::WindowlessApplicat Utility::Arguments args; args.addBooleanOption("all-extensions") .setHelp("all-extensions", "show extensions also for fully supported versions") - .addBooleanOption("no-limits") - .setHelp("no-limits", "don't display limits and implementation-defined values") + .addBooleanOption("limits") + .setHelp("limits", "display also limits and implementation-defined values") .setHelp("Displays information about Magnum engine and OpenGL capabilities."); /** @@ -73,11 +73,6 @@ MagnumInfo::MagnumInfo(const Arguments& arguments): Platform::WindowlessApplicat args.parse(arguments.argc, arguments.argv); #endif - /* Create context after parsing arguments, so the help can be displayed - without creating context */ - createContext(); - Context* c = Context::current(); - /* Pass debug output as messages to JavaScript */ #ifdef CORRADE_TARGET_NACL Utility::NaClMessageStreamBuffer buffer(this); @@ -142,12 +137,27 @@ MagnumInfo::MagnumInfo(const Arguments& arguments): Platform::WindowlessApplicat #ifdef MAGNUM_TARGET_DESKTOP_GLES Debug() << " MAGNUM_TARGET_DESKTOP_GLES"; #endif + #ifdef MAGNUM_TARGET_WEBGL + Debug() << " MAGNUM_TARGET_WEBGL"; + #endif Debug() << ""; + /* Create context here, so the context creation info is displayed at proper + place */ + createContext(); + Context* c = Context::current(); Debug() << "Vendor:" << c->vendorString(); Debug() << "Renderer:" << c->rendererString(); Debug() << "OpenGL version:" << c->version() << '(' + c->versionString() + ')'; + Debug() << "Context flags:"; + #ifndef MAGNUM_TARGET_GLES + for(const auto flag: {Context::Flag::Debug, Context::Flag::RobustAccess}) + #else + for(const auto flag: {Context::Flag::Debug}) + #endif + if(c->flags() & flag) Debug() << " " << flag; + Debug() << "Supported GLSL versions:"; const std::vector shadingLanguageVersions = c->shadingLanguageVersionStrings(); for(auto it = shadingLanguageVersions.begin(); it != shadingLanguageVersions.end(); ++it) @@ -204,7 +214,7 @@ MagnumInfo::MagnumInfo(const Arguments& arguments): Platform::WindowlessApplicat Debug() << ""; } - if(args.isSet("no-limits")) return; + if(!args.isSet("limits")) return; /* Limits and implementation-defined values */ #define _h(val) Debug() << "\n " << Extensions::GL::val::string() + std::string(":"); diff --git a/src/Magnum/Primitives/CMakeLists.txt b/src/Magnum/Primitives/CMakeLists.txt index b791282eb..6f2119939 100644 --- a/src/Magnum/Primitives/CMakeLists.txt +++ b/src/Magnum/Primitives/CMakeLists.txt @@ -53,6 +53,7 @@ set(MagnumPrimitives_HEADERS visibility.h) add_library(MagnumPrimitives ${SHARED_OR_STATIC} ${MagnumPrimitives_SRCS}) +set_target_properties(MagnumPrimitives PROPERTIES DEBUG_POSTFIX "-d") if(BUILD_STATIC_PIC) # TODO: CMake 2.8.9 has this as POSITION_INDEPENDENT_CODE property set_target_properties(MagnumPrimitives PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") diff --git a/src/Magnum/Query.cpp b/src/Magnum/Query.cpp index 649994cf8..d6bee60f2 100644 --- a/src/Magnum/Query.cpp +++ b/src/Magnum/Query.cpp @@ -123,6 +123,7 @@ template<> Int AbstractQuery::result() { return result; } +#ifndef MAGNUM_TARGET_WEBGL template<> UnsignedLong AbstractQuery::result() { CORRADE_ASSERT(!target, "AbstractQuery::result(): the query is currently running", {}); @@ -151,6 +152,7 @@ template<> Long AbstractQuery::result() { return result; } #endif +#endif void AbstractQuery::begin(GLenum target) { CORRADE_ASSERT(!this->target, "AbstractQuery::begin(): the query is already running", ); diff --git a/src/Magnum/Query.h b/src/Magnum/Query.h index 45934a5b7..30913f9fd 100644 --- a/src/Magnum/Query.h +++ b/src/Magnum/Query.h @@ -107,6 +107,8 @@ class MAGNUM_EXPORT AbstractQuery: public AbstractObject { * @requires_es_extension %Extension @es_extension{EXT,disjoint_timer_query} * for result types @ref Magnum::Int "Int", @ref Magnum::UnsignedLong "UnsignedLong" * @ref Magnum::Long "Long". + * @attention @ref Magnum::UnsignedLong "UnsignedLong" and @ref Magnum::Long "Long" + * result type is not available in @ref MAGNUM_TARGET_WEBGL "WebGL". */ template T result(); @@ -147,16 +149,18 @@ class MAGNUM_EXPORT AbstractQuery: public AbstractObject { template<> bool MAGNUM_EXPORT AbstractQuery::result(); template<> UnsignedInt MAGNUM_EXPORT AbstractQuery::result(); template<> Int MAGNUM_EXPORT AbstractQuery::result(); +#ifndef MAGNUM_TARGET_WEBGL template<> UnsignedLong MAGNUM_EXPORT AbstractQuery::result(); template<> Long MAGNUM_EXPORT AbstractQuery::result(); #endif +#endif #ifndef MAGNUM_TARGET_GLES2 /** -@brief Query for primitives and elapsed time +@brief Query for primitives Queries count of generated primitives from vertex shader, geometry shader or -transform feedback and elapsed time. Example usage: +transform feedback. Example usage: @code PrimitiveQuery q; diff --git a/src/Magnum/RectangleTexture.h b/src/Magnum/RectangleTexture.h index 2d8c76c6c..91a3fca21 100644 --- a/src/Magnum/RectangleTexture.h +++ b/src/Magnum/RectangleTexture.h @@ -86,8 +86,8 @@ class RectangleTexture: public AbstractTexture { * * Sets filter used when the object pixel size is smaller than the * texture size. If @extension{EXT,direct_state_access} is not - * available, the texture is bound to some layer before the operation. - * Initial value is @ref Sampler::Filter::Linear. + * available, the texture is bound to some texture unit before the + * operation. Initial value is @ref Sampler::Filter::Linear. * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and @fn_gl{TexParameter} * or @fn_gl_extension{TextureParameter,EXT,direct_state_access} * with @def_gl{TEXTURE_MIN_FILTER} @@ -103,20 +103,17 @@ class RectangleTexture: public AbstractTexture { return *this; } - #ifndef MAGNUM_TARGET_GLES /** * @brief %Image size * * The result is not cached in any way. If * @extension{EXT,direct_state_access} is not available, the texture - * is bound to some layer before the operation. + * is bound to some texture unit before the operation. * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and * @fn_gl{GetTexLevelParameter} or @fn_gl_extension{GetTextureLevelParameter,EXT,direct_state_access} * with @def_gl{TEXTURE_WIDTH} and @def_gl{TEXTURE_HEIGHT} - * @requires_gl %Texture image queries are not available in OpenGL ES. */ Vector2i imageSize() { return DataHelper<2>::imageSize(*this, _target, 0); } - #endif /** * @brief Set wrapping @@ -124,9 +121,9 @@ class RectangleTexture: public AbstractTexture { * @return Reference to self (for method chaining) * * Sets wrapping type for coordinates out of (0, textureSizeInGivenDirection-1) - * range for rectangle textures. If @extension{EXT,direct_state_access} - * is not available, the texture is bound to some layer before the - * operation. Initial value is @ref Sampler::Wrapping::ClampToEdge. + * range. If @extension{EXT,direct_state_access} is not available, the + * texture is bound to some texture unit before the operation. Initial + * value is @ref Sampler::Wrapping::ClampToEdge. * @attention Only @ref Sampler::Wrapping::ClampToEdge and * @ref Sampler::Wrapping::ClampToBorder is supported on this * texture type. @@ -146,6 +143,20 @@ class RectangleTexture: public AbstractTexture { return *this; } + #ifndef MAGNUM_TARGET_GLES + /** @copydoc Texture::setBorderColor(const Vector4ui&) */ + RectangleTexture& setBorderColor(const Vector4ui& color) { + AbstractTexture::setBorderColor(color); + return *this; + } + + /** @copydoc Texture::setBorderColor(const Vector4i&) */ + RectangleTexture& setBorderColor(const Vector4i& color) { + AbstractTexture::setBorderColor(color); + return *this; + } + #endif + /** @copydoc Texture::setMaxAnisotropy() */ RectangleTexture& setMaxAnisotropy(Float anisotropy) { AbstractTexture::setMaxAnisotropy(anisotropy); @@ -155,7 +166,7 @@ class RectangleTexture: public AbstractTexture { /** * @brief Set storage * @param internalFormat Internal format - * @param size Size + * @param size %Texture size * @return Reference to self (for method chaining) * * Specifies entire structure of a texture at once, removing the need @@ -165,10 +176,10 @@ class RectangleTexture: public AbstractTexture { * allowed. * * If @extension{EXT,direct_state_access} is not available, the texture - * is bound to some layer before the operation. If @extension{ARB,texture_storage} - * (part of OpenGL 4.2), OpenGL ES 3.0 or @es_extension{EXT,texture_storage} - * in OpenGL ES 2.0 is not available, the feature is emulated with - * @ref setImage() call. + * is bound to some texture unit before the operation. If + * @extension{ARB,texture_storage} (part of OpenGL 4.2), OpenGL ES 3.0 + * or @es_extension{EXT,texture_storage} in OpenGL ES 2.0 is not + * available, the feature is emulated with @ref setImage() call. * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and @fn_gl{TexStorage2D} * or @fn_gl_extension{TextureStorage2D,EXT,direct_state_access}, * eventually @fn_gl{TexImage2D} or @@ -189,7 +200,7 @@ class RectangleTexture: public AbstractTexture { * @ref imageSize(). * * If @extension{EXT,direct_state_access} is not available, the - * texture is bound to some layer before the operation. If + * texture is bound to some texture unit before the operation. If * @extension{ARB,robustness} is available, the operation is protected * from buffer overflow. However, if both @extension{EXT,direct_state_access} * and @extension{ARB,robustness} are available, the DSA version is @@ -230,7 +241,7 @@ class RectangleTexture: public AbstractTexture { * use @ref setStorage() and @ref setSubImage() instead. * * If @extension{EXT,direct_state_access} is not available, the - * texture is bound to some layer before the operation. + * texture is bound to some texture unit before the operation. * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and @fn_gl{TexImage2D} * or @fn_gl_extension{TextureImage2D,EXT,direct_state_access} */ @@ -260,7 +271,7 @@ class RectangleTexture: public AbstractTexture { * @return Reference to self (for method chaining) * * If @extension{EXT,direct_state_access} is not available, the - * texture is bound to some layer before the operation. + * texture is bound to some texture unit before the operation. * @see @ref setStorage(), @ref setImage(), @fn_gl{ActiveTexture}, * @fn_gl{BindTexture} and @fn_gl{TexSubImage2D} or * @fn_gl_extension{TextureSubImage2D,EXT,direct_state_access} @@ -286,8 +297,8 @@ class RectangleTexture: public AbstractTexture { /** * @brief Invalidate texture image * - * If running on OpenGL ES or extension @extension{ARB,invalidate_subdata} - * (part of OpenGL 4.3) is not available, this function does nothing. + * If extension @extension{ARB,invalidate_subdata} (part of OpenGL 4.3) + * is not available, this function does nothing. * @see @ref invalidateSubImage(), @fn_gl{InvalidateTexImage} */ void invalidateImage() { AbstractTexture::invalidateImage(0); } @@ -297,8 +308,8 @@ class RectangleTexture: public AbstractTexture { * @param offset Offset into the texture * @param size Size of invalidated data * - * If running on OpenGL ES or extension @extension{ARB,invalidate_subdata} - * (part of OpenGL 4.3) is not available, this function does nothing. + * If extension @extension{ARB,invalidate_subdata} (part of OpenGL 4.3) + * is not available, this function does nothing. * @see @ref invalidateImage(), @fn_gl{InvalidateTexSubImage} */ void invalidateSubImage(const Vector2i& offset, const Vector2i& size) { diff --git a/src/Magnum/Renderer.h b/src/Magnum/Renderer.h index d36e956b5..ff2226294 100644 --- a/src/Magnum/Renderer.h +++ b/src/Magnum/Renderer.h @@ -519,6 +519,9 @@ class MAGNUM_EXPORT Renderer { * @param mask Mask for both reference and buffer value. * Initial value is all `1`s. * + * @attention In @ref MAGNUM_TARGET_WEBGL "WebGL" the reference value + * and mask must be the same for both front and back polygon + * facing. * @see @ref Feature::StencilTest, @ref setStencilFunction(StencilFunction, Int, UnsignedInt), * @ref setStencilOperation(), @fn_gl{StencilFuncSeparate} */ @@ -606,6 +609,9 @@ class MAGNUM_EXPORT Renderer { * * Set given bit to `0` to disallow writing stencil value for given * faces to it. Initial value is all `1`s. + * + * @attention In @ref MAGNUM_TARGET_WEBGL "WebGL" the mask must be the + * same for both front and back polygon facing. * @see setStencilMask(UnsignedInt), setColorMask(), setDepthMask(), * @fn_gl{StencilMaskSeparate} */ @@ -812,6 +818,9 @@ class MAGNUM_EXPORT Renderer { * @param destination How the destination blending factor is * computed from framebuffer. Initial value is @ref BlendFunction::Zero. * + * @attention In @ref MAGNUM_TARGET_WEBGL "WebGL", constant color and + * constant alpha cannot be used together as source and + * destination factors. * @see @ref Feature::Blending, @ref setBlendFunction(BlendFunction, BlendFunction, BlendFunction, BlendFunction), * @ref setBlendEquation(), @ref setBlendColor(), * @fn_gl{BlendFunc} diff --git a/src/Magnum/ResourceManager.h b/src/Magnum/ResourceManager.h index a4b2ce0e5..d700b6c51 100644 --- a/src/Magnum/ResourceManager.h +++ b/src/Magnum/ResourceManager.h @@ -212,7 +212,7 @@ if(!cube) { @endcode - Using the resource data. @code -shader->setTexture(layer); +shader->setTexture(*texture); cube->draw(*shader); @endcode - Destroying resource references and deleting manager instance when nothing diff --git a/src/Magnum/Sampler.h b/src/Magnum/Sampler.h index b2c6e995a..51a6ba890 100644 --- a/src/Magnum/Sampler.h +++ b/src/Magnum/Sampler.h @@ -150,13 +150,6 @@ class MAGNUM_EXPORT Sampler { * instead. */ static CORRADE_DEPRECATED("use maxMaxAnisotropy() instead") Float maxAnisotropy() { return maxMaxAnisotropy(); } - - /** - * @copybrief maxMaxAnisotropy() - * @deprecated Use @ref Magnum::Sampler::maxMaxAnisotropy() "maxMaxAnisotropy()" - * instead. - */ - static CORRADE_DEPRECATED("use maxMaxAnisotropy() instead") Float maxSupportedAnisotropy() { return maxMaxAnisotropy(); } #endif }; diff --git a/src/Magnum/SceneGraph/CMakeLists.txt b/src/Magnum/SceneGraph/CMakeLists.txt index 4eaf4e304..4b4c42b4b 100644 --- a/src/Magnum/SceneGraph/CMakeLists.txt +++ b/src/Magnum/SceneGraph/CMakeLists.txt @@ -83,6 +83,7 @@ endif() add_library(MagnumSceneGraph ${SHARED_OR_STATIC} $ ${MagnumSceneGraph_GracefulAssert_SRCS}) +set_target_properties(MagnumSceneGraph PROPERTIES DEBUG_POSTFIX "-d") target_link_libraries(MagnumSceneGraph Magnum) install(TARGETS MagnumSceneGraph @@ -96,7 +97,9 @@ if(BUILD_TESTS) add_library(MagnumSceneGraphTestLib ${SHARED_OR_STATIC} $ ${MagnumSceneGraph_GracefulAssert_SRCS}) - set_target_properties(MagnumSceneGraphTestLib PROPERTIES COMPILE_FLAGS "-DCORRADE_GRACEFUL_ASSERT -DMagnumSceneGraph_EXPORTS") + set_target_properties(MagnumSceneGraphTestLib PROPERTIES + COMPILE_FLAGS "-DCORRADE_GRACEFUL_ASSERT -DMagnumSceneGraph_EXPORTS" + DEBUG_POSTFIX "-d") target_link_libraries(MagnumSceneGraphTestLib MagnumMathTestLib) # On Windows we need to install first and then run the tests to avoid "DLL diff --git a/src/Magnum/SceneGraph/Object.hpp b/src/Magnum/SceneGraph/Object.hpp index 150175efa..92b43ce7f 100644 --- a/src/Magnum/SceneGraph/Object.hpp +++ b/src/Magnum/SceneGraph/Object.hpp @@ -53,8 +53,15 @@ template void AbstractObject::se #ifdef CORRADE_GCC47_COMPATIBILITY template void AbstractObject::setClean(std::initializer_list*> objects) { + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + #endif /* GCC 4.5 doesn't like {} here */ return setClean(std::vector*>(objects)); + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif } #endif #endif @@ -77,8 +84,15 @@ template auto AbstractObject::tr #ifdef CORRADE_GCC47_COMPATIBILITY template auto AbstractObject::transformationMatrices(std::initializer_list*> objects, const MatrixType& initialTransformationMatrix) const -> std::vector { + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + #endif /* GCC 4.5 doesn't like {} here */ return transformationMatrices(std::vector*>(objects), initialTransformationMatrix); + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif } #endif #endif @@ -242,8 +256,15 @@ template auto Object::transformationMatric #ifdef CORRADE_GCC47_COMPATIBILITY template auto Object::transformationMatrices(std::initializer_list*> objects, const MatrixType& initialTransformationMatrix) const -> std::vector { + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + #endif /* GCC 4.5 doesn't like {} here */ return transformationMatrices(std::vector*>(objects), initialTransformationMatrix); + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif } #endif #endif @@ -379,8 +400,15 @@ template std::vector Ob #ifdef CORRADE_GCC47_COMPATIBILITY template std::vector Object::transformations(std::initializer_list*> objects, const typename Transformation::DataType& initialTransformation) const { + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + #endif /* GCC 4.5 doesn't like {} here */ return transformations(std::vector*>(objects), initialTransformation); + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif } #endif #endif @@ -502,8 +530,15 @@ template void Object::setClean(const std:: #ifdef CORRADE_GCC47_COMPATIBILITY template void Object::setClean(std::initializer_list*> objects) { + #ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + #endif /* GCC 4.5 doesn't like {} here */ setClean(std::vector*>(objects)); + #ifdef __GNUC__ + #pragma GCC diagnostic pop + #endif } #endif #endif diff --git a/src/Magnum/Shader.cpp b/src/Magnum/Shader.cpp index defdb90bc..a0e76531a 100644 --- a/src/Magnum/Shader.cpp +++ b/src/Magnum/Shader.cpp @@ -37,7 +37,7 @@ #include "Implementation/State.h" #include "Implementation/ShaderState.h" -#if defined(CORRADE_TARGET_NACL_NEWLIB) || defined(__MINGW32__) +#if defined(CORRADE_TARGET_NACL_NEWLIB) || defined(CORRADE_TARGET_ANDROID) || defined(__MINGW32__) #include #endif @@ -556,7 +556,7 @@ Shader::Shader(const Version version, const Type type): _type(type), _id(0) { case Version::GL440: _sources.push_back("#version 440\n"); return; #else case Version::GLES200: _sources.push_back("#version 100\n"); return; - case Version::GLES300: _sources.push_back("#version 300\n"); return; + case Version::GLES300: _sources.push_back("#version 300 es\n"); return; #endif /* The user is responsible for (not) adding #version directive */ @@ -594,7 +594,8 @@ std::vector Shader::sources() const { return _sources; } Shader& Shader::addSource(std::string source) { if(!source.empty()) { - #if defined(CORRADE_TARGET_NACL_NEWLIB) || defined(__MINGW32__) + /** @todo Remove when newlib has this fixed (also the include above) */ + #if defined(CORRADE_TARGET_NACL_NEWLIB) || defined(CORRADE_TARGET_ANDROID) || defined(__MINGW32__) std::ostringstream converter; converter << (_sources.size()+1)/2; #endif @@ -603,7 +604,7 @@ Shader& Shader::addSource(std::string source) { Source 0 is the #version string added in constructor. */ _sources.push_back("#line 1 " + /* This shouldn't be ambiguous. But is. */ - #if !defined(CORRADE_TARGET_NACL_NEWLIB) && !defined(__MINGW32__) + #if !defined(CORRADE_TARGET_NACL_NEWLIB) && !defined(CORRADE_TARGET_ANDROID) && !defined(__MINGW32__) #ifndef CORRADE_GCC44_COMPATIBILITY std::to_string((_sources.size()+1)/2) + #else @@ -627,57 +628,75 @@ Shader& Shader::addFile(const std::string& filename) { return *this; } -bool Shader::compile() { - CORRADE_ASSERT(_sources.size() > 1, "Shader::compile(): no files added", false); +bool Shader::compile(std::initializer_list> shaders) { + bool allSuccess = true; - /* Array of source string pointers and their lengths */ - /** @todo Use `Containers::ArrayTuple` to avoid one allocation if it ever - gets to be implemented (we need properly aligned memory too) */ - Containers::Array pointers(_sources.size()); - Containers::Array sizes(_sources.size()); - for(std::size_t i = 0; i != _sources.size(); ++i) { - pointers[i] = static_cast(_sources[i].data()); - sizes[i] = _sources[i].size(); + /* Allocate large enough array for source pointers and sizes (to avoid + reallocating it for each of them) */ + std::size_t maxSourceCount = 0; + for(Shader& shader: shaders) { + CORRADE_ASSERT(shader._sources.size() > 1, "Shader::compile(): no files added", false); + maxSourceCount = std::max(shader._sources.size(), maxSourceCount); } + Containers::Array pointers(maxSourceCount); + Containers::Array sizes(maxSourceCount); - /* Create shader and set its source */ - glShaderSource(_id, _sources.size(), pointers, sizes); - - /* Compile shader */ - glCompileShader(_id); - - /* Check compilation status */ - GLint success, logLength; - glGetShaderiv(_id, GL_COMPILE_STATUS, &success); - glGetShaderiv(_id, GL_INFO_LOG_LENGTH, &logLength); - - /* Error or warning message. The string is returned null-terminated, scrap - the \0 at the end afterwards */ - std::string message(logLength, '\0'); - if(message.size() > 1) - glGetShaderInfoLog(_id, message.size(), nullptr, &message[0]); - message.resize(std::max(logLength, 1)-1); - - /* Show error log */ - if(!success) { - Error out; - out.setFlag(Debug::NewLineAtTheEnd, false); - out.setFlag(Debug::SpaceAfterEachValue, false); - out << "Shader: " << shaderName(_type) - << " shader failed to compile with the following message:\n" - << message; - - /* Or just message, if any */ - } else if(!message.empty()) { - Error out; - out.setFlag(Debug::NewLineAtTheEnd, false); - out.setFlag(Debug::SpaceAfterEachValue, false); - out << "Shader: " << shaderName(_type) - << " shader was successfully compiled with the following message:\n" - << message; + /* Upload sources of all shaders */ + for(Shader& shader: shaders) { + for(std::size_t i = 0; i != shader._sources.size(); ++i) { + pointers[i] = static_cast(shader._sources[i].data()); + sizes[i] = shader._sources[i].size(); + } + + glShaderSource(shader._id, shader._sources.size(), pointers, sizes); + } + + /* Invoke (possibly parallel) compilation on all shaders */ + for(Shader& shader: shaders) glCompileShader(shader._id); + + /* After compilation phase, check status of all shaders */ + Int i = 1; + for(Shader& shader: shaders) { + GLint success, logLength; + glGetShaderiv(shader._id, GL_COMPILE_STATUS, &success); + glGetShaderiv(shader._id, GL_INFO_LOG_LENGTH, &logLength); + + /* Error or warning message. The string is returned null-terminated, + scrap the \0 at the end afterwards */ + std::string message(logLength, '\0'); + if(message.size() > 1) + glGetShaderInfoLog(shader._id, message.size(), nullptr, &message[0]); + message.resize(std::max(logLength, 1)-1); + + /* Show error log */ + if(!success) { + Error out; + out.setFlag(Debug::NewLineAtTheEnd, false); + out.setFlag(Debug::SpaceAfterEachValue, false); + out << "Shader::compile(): compilation of " << shaderName(shader._type) + << " shader"; + if(shaders.size() != 1) out << ' ' << std::to_string(i); + out << " failed with the following message:\n" + << message; + + /* Or just warnings, if any */ + } else if(!message.empty()) { + Warning out; + out.setFlag(Debug::NewLineAtTheEnd, false); + out.setFlag(Debug::SpaceAfterEachValue, false); + out << "Shader::compile(): compilation of " << shaderName(shader._type) + << " shader"; + if(shaders.size() != 1) out << ' ' << std::to_string(i); + out << " succeeded with the following message:\n" + << message; + } + + /* Success of all depends on each of them */ + allSuccess = allSuccess && success; + ++i; } - return success; + return allSuccess; } #ifndef DOXYGEN_GENERATING_OUTPUT diff --git a/src/Magnum/Shader.h b/src/Magnum/Shader.h index 0c64d26ab..c7fe26991 100644 --- a/src/Magnum/Shader.h +++ b/src/Magnum/Shader.h @@ -29,8 +29,9 @@ * @brief Class @ref Magnum::Shader */ -#include +#include #include +#include #include "Magnum/AbstractObject.h" #include "Magnum/Magnum.h" @@ -378,8 +379,8 @@ class MAGNUM_EXPORT Shader: public AbstractObject { * * The result is cached, repeated queries don't result in repeated * OpenGL calls. - * @see @ref AbstractTexture::maxLayers(), @ref maxTextureImageUnits(), - * @fn_gl{Get} with @def_gl{MAX_COMBINED_TEXTURE_IMAGE_UNITS} + * @see @ref maxTextureImageUnits(), @fn_gl{Get} with + * @def_gl{MAX_COMBINED_TEXTURE_IMAGE_UNITS} */ static Int maxCombinedTextureImageUnits(); @@ -435,6 +436,20 @@ class MAGNUM_EXPORT Shader: public AbstractObject { static Int maxCombinedUniformComponents(Type type); #endif + /** + * @brief Compile multiple shaders simultaenously + * + * Returns `false` if compilation of any shader failed, `true` if + * everything succeeded. Compiler messages (if any) are printed to + * error output. The operation is batched in a way that allows the + * driver to perform multiple compilations simultaenously (i.e. in + * multiple threads). + * @see @fn_gl{ShaderSource}, @fn_gl{CompileShader}, @fn_gl{GetShader} + * with @def_gl{COMPILE_STATUS} and @def_gl{INFO_LOG_LENGTH}, + * @fn_gl{GetShaderInfoLog} + */ + static bool compile(std::initializer_list> shaders); + /** * @brief Constructor * @param version Target version @@ -528,13 +543,12 @@ class MAGNUM_EXPORT Shader: public AbstractObject { /** * @brief Compile shader * - * Returns `false` if compilation failed, `true` otherwise. Compiler - * message (if any) is printed to error output. - * @see @fn_gl{ShaderSource}, @fn_gl{CompileShader}, @fn_gl{GetShader} - * with @def_gl{COMPILE_STATUS} and @def_gl{INFO_LOG_LENGTH}, - * @fn_gl{GetShaderInfoLog} + * Compiles single shader. Prefer to compile multiple shaders at once + * using @ref compile(std::initializer_list>) + * for improved performance, see its documentation for more + * information. */ - bool compile(); + bool compile() { return compile({*this}); } private: Type _type; diff --git a/src/Magnum/Shaders/AbstractVector.h b/src/Magnum/Shaders/AbstractVector.h index a1a843bf4..589887a6a 100644 --- a/src/Magnum/Shaders/AbstractVector.h +++ b/src/Magnum/Shaders/AbstractVector.h @@ -49,7 +49,7 @@ template class AbstractVector: public AbstractShaderProg #ifdef MAGNUM_BUILD_DEPRECATED enum: Int { /** - * Layer for vector texture + * Vector texture binding unit * @deprecated Use @ref Magnum::Shaders::AbstractVector::setVectorTexture() "setVectorTexture()" instead. */ VectorTextureLayer = 16 diff --git a/src/Magnum/Shaders/CMakeLists.txt b/src/Magnum/Shaders/CMakeLists.txt index 8b2e16a16..6ffa51558 100644 --- a/src/Magnum/Shaders/CMakeLists.txt +++ b/src/Magnum/Shaders/CMakeLists.txt @@ -60,6 +60,7 @@ if(BUILD_STATIC) endif() add_library(MagnumShaders ${SHARED_OR_STATIC} ${MagnumShaders_SRCS}) +set_target_properties(MagnumShaders PROPERTIES DEBUG_POSTFIX "-d") if(BUILD_STATIC_PIC) # TODO: CMake 2.8.9 has this as POSITION_INDEPENDENT_CODE property set_target_properties(MagnumShaders PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") diff --git a/src/Magnum/Shaders/DistanceFieldVector.cpp b/src/Magnum/Shaders/DistanceFieldVector.cpp index 88f2a1236..31cfceb6d 100644 --- a/src/Magnum/Shaders/DistanceFieldVector.cpp +++ b/src/Magnum/Shaders/DistanceFieldVector.cpp @@ -54,14 +54,15 @@ template DistanceFieldVector::DistanceFieldV Version version = Context::current()->supportedVersion(vs); Shader frag = Implementation::createCompatibilityShader(version, Shader::Type::Vertex); + Shader vert = Implementation::createCompatibilityShader(version, Shader::Type::Fragment); + frag.addSource(rs.get("generic.glsl")) .addSource(rs.get(vertexShaderName())); - CORRADE_INTERNAL_ASSERT_OUTPUT(frag.compile()); - AbstractShaderProgram::attachShader(frag); - - Shader vert = Implementation::createCompatibilityShader(version, Shader::Type::Fragment); vert.addSource(rs.get("DistanceFieldVector.frag")); - CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile()); + + CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({frag, vert})); + + AbstractShaderProgram::attachShader(frag); AbstractShaderProgram::attachShader(vert); #ifndef MAGNUM_TARGET_GLES diff --git a/src/Magnum/Shaders/Flat.cpp b/src/Magnum/Shaders/Flat.cpp index 60c77ca6e..43fee89a8 100644 --- a/src/Magnum/Shaders/Flat.cpp +++ b/src/Magnum/Shaders/Flat.cpp @@ -57,16 +57,17 @@ template Flat::Flat(const Flags flags): tran Version version = Context::current()->supportedVersion(vs); Shader vert = Implementation::createCompatibilityShader(version, Shader::Type::Vertex); + Shader frag = Implementation::createCompatibilityShader(version, Shader::Type::Fragment); + vert.addSource(flags & Flag::Textured ? "#define TEXTURED\n" : "") .addSource(rs.get("generic.glsl")) .addSource(rs.get(vertexShaderName())); - CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile()); - attachShader(vert); - - Shader frag = Implementation::createCompatibilityShader(version, Shader::Type::Fragment); frag.addSource(flags & Flag::Textured ? "#define TEXTURED\n" : "") .addSource(rs.get("Flat.frag")); - CORRADE_INTERNAL_ASSERT_OUTPUT(frag.compile()); + + CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag})); + + attachShader(vert); attachShader(frag); #ifndef MAGNUM_TARGET_GLES diff --git a/src/Magnum/Shaders/Flat.h b/src/Magnum/Shaders/Flat.h index e64afdd2a..f4cdde677 100644 --- a/src/Magnum/Shaders/Flat.h +++ b/src/Magnum/Shaders/Flat.h @@ -74,7 +74,8 @@ template class MAGNUM_SHADERS_EXPORT Flat: public Abstra #ifdef MAGNUM_BUILD_DEPRECATED enum: Int { /** - * Layer for color texture. Used only if @ref Flag::Textured is set. + * Color texture binding unit. Used only if @ref Flag::Textured is + * set. * @deprecated use @ref Magnum::Shaders::Flat::setTexture() "setTexture()" instead. */ TextureLayer = 0 diff --git a/src/Magnum/Shaders/Generic.h b/src/Magnum/Shaders/Generic.h index 315a0e851..567fa6ab1 100644 --- a/src/Magnum/Shaders/Generic.h +++ b/src/Magnum/Shaders/Generic.h @@ -82,10 +82,10 @@ template struct Generic { }; #endif -/** @brief Generic 2D shader definition */ +/** @brief %Generic 2D shader definition */ typedef Generic<2> Generic2D; -/** @brief Generic 3D shader definition */ +/** @brief %Generic 3D shader definition */ typedef Generic<3> Generic3D; #ifndef DOXYGEN_GENERATING_OUTPUT diff --git a/src/Magnum/Shaders/Implementation/CreateCompatibilityShader.cpp b/src/Magnum/Shaders/Implementation/CreateCompatibilityShader.cpp index 1377e6976..04accbfa1 100644 --- a/src/Magnum/Shaders/Implementation/CreateCompatibilityShader.cpp +++ b/src/Magnum/Shaders/Implementation/CreateCompatibilityShader.cpp @@ -44,6 +44,13 @@ Shader createCompatibilityShader(const Version version, const Shader::Type type) shader.addSource("#define DISABLE_GL_ARB_explicit_uniform_location\n"); #endif + /* My Android emulator (running on NVidia) doesn't define GL_ES + preprocessor macro, thus *all* the stock shaders fail to compile */ + /** @todo remove this when Android emulator is sane */ + #ifdef CORRADE_TARGET_ANDROID + shader.addSource("#ifndef GL_ES\n#define GL_ES 1\n#endif\n"); + #endif + shader.addSource(Utility::Resource("MagnumShaders").get("compatibility.glsl")); return shader; } diff --git a/src/Magnum/Shaders/MeshVisualizer.cpp b/src/Magnum/Shaders/MeshVisualizer.cpp index 660929260..c0d50e681 100644 --- a/src/Magnum/Shaders/MeshVisualizer.cpp +++ b/src/Magnum/Shaders/MeshVisualizer.cpp @@ -30,6 +30,7 @@ #include "Magnum/Context.h" #include "Magnum/Extensions.h" #include "Magnum/Shader.h" +#include "MagnumExternal/Optional/optional.hpp" #include "Implementation/CreateCompatibilityShader.h" @@ -56,31 +57,35 @@ MeshVisualizer::MeshVisualizer(const Flags flags): flags(flags), transformationP #endif Shader vert = Implementation::createCompatibilityShader(version, Shader::Type::Vertex); + Shader frag = Implementation::createCompatibilityShader(version, Shader::Type::Fragment); + vert.addSource(flags & Flag::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") .addSource(flags & Flag::NoGeometryShader ? "#define NO_GEOMETRY_SHADER\n" : "") .addSource(rs.get("generic.glsl")) .addSource(rs.get("MeshVisualizer.vert")); - CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile()); - vert.compile(); - attachShader(vert); + frag.addSource(flags & Flag::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") + .addSource(flags & Flag::NoGeometryShader ? "#define NO_GEOMETRY_SHADER\n" : "") + .addSource(rs.get("MeshVisualizer.frag")); #ifndef MAGNUM_TARGET_GLES + std::optional geom; if(flags & Flag::Wireframe && !(flags & Flag::NoGeometryShader)) { - Shader geom = Implementation::createCompatibilityShader(version, Shader::Type::Geometry); - geom.addSource(rs.get("MeshVisualizer.geom")); - CORRADE_INTERNAL_ASSERT_OUTPUT(geom.compile()); - geom.compile(); - attachShader(geom); + geom = Implementation::createCompatibilityShader(version, Shader::Type::Geometry); + geom->addSource(rs.get("MeshVisualizer.geom")); } #endif - Shader frag = Implementation::createCompatibilityShader(version, Shader::Type::Fragment); - frag.addSource(flags & Flag::Wireframe ? "#define WIREFRAME_RENDERING\n" : "") - .addSource(flags & Flag::NoGeometryShader ? "#define NO_GEOMETRY_SHADER\n" : "") - .addSource(rs.get("MeshVisualizer.frag")); - CORRADE_INTERNAL_ASSERT_OUTPUT(frag.compile()); - frag.compile(); + #ifndef MAGNUM_TARGET_GLES + if(geom) Shader::compile({vert, *geom, frag}); + else + #endif + Shader::compile({vert, frag}); + + attachShader(vert); attachShader(frag); + #ifndef MAGNUM_TARGET_GLES + if(geom) attachShader(*geom); + #endif #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported(version)) @@ -99,7 +104,6 @@ MeshVisualizer::MeshVisualizer(const Flags flags): flags(flags), transformationP } CORRADE_INTERNAL_ASSERT_OUTPUT(link()); - link(); #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported(version)) diff --git a/src/Magnum/Shaders/Phong.cpp b/src/Magnum/Shaders/Phong.cpp index 48f34597c..21f346d16 100644 --- a/src/Magnum/Shaders/Phong.cpp +++ b/src/Magnum/Shaders/Phong.cpp @@ -54,18 +54,19 @@ Phong::Phong(const Flags flags): transformationMatrixUniform(0), projectionMatri #endif Shader vert = Implementation::createCompatibilityShader(version, Shader::Type::Vertex); + Shader frag = Implementation::createCompatibilityShader(version, Shader::Type::Fragment); + vert.addSource(flags ? "#define TEXTURED\n" : "") .addSource(rs.get("generic.glsl")) .addSource(rs.get("Phong.vert")); - CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile()); - attachShader(vert); - - Shader frag = Implementation::createCompatibilityShader(version, Shader::Type::Fragment); frag.addSource(flags & Flag::AmbientTexture ? "#define AMBIENT_TEXTURE\n" : "") .addSource(flags & Flag::DiffuseTexture ? "#define DIFFUSE_TEXTURE\n" : "") .addSource(flags & Flag::SpecularTexture ? "#define SPECULAR_TEXTURE\n" : "") .addSource(rs.get("Phong.frag")); - CORRADE_INTERNAL_ASSERT_OUTPUT(frag.compile()); + + CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag})); + + attachShader(vert); attachShader(frag); #ifndef MAGNUM_TARGET_GLES @@ -129,4 +130,9 @@ Phong& Phong::setSpecularTexture(Texture2D& texture) { return *this; } +Phong& Phong::setTextures(Texture2D* ambient, Texture2D* diffuse, Texture2D* specular) { + AbstractTexture::bind(AmbientTextureLayer, {ambient, diffuse, specular}); + return *this; +} + }} diff --git a/src/Magnum/Shaders/Phong.h b/src/Magnum/Shaders/Phong.h index f07967cfc..902496ca7 100644 --- a/src/Magnum/Shaders/Phong.h +++ b/src/Magnum/Shaders/Phong.h @@ -65,24 +65,24 @@ class MAGNUM_SHADERS_EXPORT Phong: public AbstractShaderProgram { #ifdef MAGNUM_BUILD_DEPRECATED enum: Int { /** - * Layer for ambient texture. Used only if @ref Flag::AmbientTexture - * is set. + * Ambient texture binding unit. Used only if + * @ref Flag::AmbientTexture is set. * @deprecated Use @ref Magnum::Shaders::Phong::setAmbientTexture() "setAmbientTexture()" * instead. */ AmbientTextureLayer = 0, /** - * Layer for diffuse texture. Used only if @ref Flag::DiffuseTexture - * is set. + * Diffuse texture binding unit. Used only if + * @ref Flag::DiffuseTexture is set. * @deprecated Use @ref Magnum::Shaders::Phong::setDiffuseTexture() "setDiffuseTexture()" * instead. */ DiffuseTextureLayer = 1, /** - * Layer for specular texture. Used only if @ref Flag::SpecularTexture - * is set. + * Specular texture binding unit. Used only if + * @ref Flag::SpecularTexture is set. * @deprecated Use @ref Magnum::Shaders::Phong::setSpecularTexture() "setSpecularTexture()" * instead. */ @@ -132,7 +132,7 @@ class MAGNUM_SHADERS_EXPORT Phong: public AbstractShaderProgram { * @return Reference to self (for method chaining) * * Has effect only if @ref Flag::AmbientTexture is set. - * @see @ref setAmbientColor() + * @see @ref setTextures(), @ref setAmbientColor() */ Phong& setAmbientTexture(Texture2D& texture); @@ -150,7 +150,7 @@ class MAGNUM_SHADERS_EXPORT Phong: public AbstractShaderProgram { * @return Reference to self (for method chaining) * * Has effect only if @ref Flag::DiffuseTexture is set. - * @see @ref setDiffuseColor() + * @see @ref setTextures(), @ref setDiffuseColor() */ Phong& setDiffuseTexture(Texture2D& texture); @@ -169,10 +169,22 @@ class MAGNUM_SHADERS_EXPORT Phong: public AbstractShaderProgram { * @return Reference to self (for method chaining) * * Has effect only if @ref Flag::SpecularTexture is set. - * @see @ref setSpecularColor() + * @see @ref setTextures(), @ref setSpecularColor() */ Phong& setSpecularTexture(Texture2D& texture); + /** + * @brief Set textures + * @return Reference to self (for method chaining) + * + * A particular texture has effect only if particular texture flag from + * @ref Phong::Flag "Flag" is set, you can use `nullptr` for the rest. + * More efficient than setting each texture separately. + * @see @ref setAmbientTexture(), @ref setDiffuseTexture(), + * @ref setSpecularTexture() + */ + Phong& setTextures(Texture2D* ambient, Texture2D* diffuse, Texture2D* specular); + /** * @brief Set shininess * @return Reference to self (for method chaining) @@ -197,6 +209,9 @@ class MAGNUM_SHADERS_EXPORT Phong: public AbstractShaderProgram { /** * @brief Set normal matrix * @return Reference to self (for method chaining) + * + * The matrix doesn't need to be normalized, as the renormalization + * must be done in the shader anyway. */ Phong& setNormalMatrix(const Matrix3x3& matrix) { setUniform(normalMatrixUniform, matrix); diff --git a/src/Magnum/Shaders/Vector.cpp b/src/Magnum/Shaders/Vector.cpp index 459a90595..c42bbdadb 100644 --- a/src/Magnum/Shaders/Vector.cpp +++ b/src/Magnum/Shaders/Vector.cpp @@ -54,14 +54,15 @@ template Vector::Vector(): transformationPro Version version = Context::current()->supportedVersion(vs); Shader vert = Implementation::createCompatibilityShader(version, Shader::Type::Vertex); + Shader frag = Implementation::createCompatibilityShader(version, Shader::Type::Fragment); + vert.addSource(rs.get("generic.glsl")) .addSource(rs.get(vertexShaderName())); - CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile()); - AbstractShaderProgram::attachShader(vert); - - Shader frag = Implementation::createCompatibilityShader(version, Shader::Type::Fragment); frag.addSource(rs.get("Vector.frag")); - CORRADE_INTERNAL_ASSERT_OUTPUT(frag.compile()); + + CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag})); + + AbstractShaderProgram::attachShader(vert); AbstractShaderProgram::attachShader(frag); #ifndef MAGNUM_TARGET_GLES diff --git a/src/Magnum/Shaders/VertexColor.cpp b/src/Magnum/Shaders/VertexColor.cpp index 282f452f2..0eb9306a1 100644 --- a/src/Magnum/Shaders/VertexColor.cpp +++ b/src/Magnum/Shaders/VertexColor.cpp @@ -62,14 +62,15 @@ template VertexColor::VertexColor(): transfo #endif Shader vert = Implementation::createCompatibilityShader(version, Shader::Type::Vertex); + Shader frag = Implementation::createCompatibilityShader(version, Shader::Type::Fragment); + vert.addSource(rs.get("generic.glsl")) .addSource(rs.get(vertexShaderName())); - CORRADE_INTERNAL_ASSERT_OUTPUT(vert.compile()); - attachShader(vert); - - Shader frag = Implementation::createCompatibilityShader(version, Shader::Type::Fragment); frag.addSource(rs.get("VertexColor.frag")); - CORRADE_INTERNAL_ASSERT_OUTPUT(frag.compile()); + + CORRADE_INTERNAL_ASSERT_OUTPUT(Shader::compile({vert, frag})); + + attachShader(vert); attachShader(frag); #ifndef MAGNUM_TARGET_GLES diff --git a/src/Magnum/Shapes/AbstractShape.cpp b/src/Magnum/Shapes/AbstractShape.cpp index 913ed01e9..8bb10797a 100644 --- a/src/Magnum/Shapes/AbstractShape.cpp +++ b/src/Magnum/Shapes/AbstractShape.cpp @@ -27,6 +27,7 @@ #include +#include "Magnum/Shapes/Collision.h" #include "Magnum/Shapes/ShapeGroup.h" #include "Magnum/Shapes/Implementation/CollisionDispatch.h" @@ -52,6 +53,10 @@ template bool AbstractShape::collides(const return Implementation::collides(abstractTransformedShape(), other.abstractTransformedShape()); } +template Collision AbstractShape::collision(const AbstractShape& other) const { + return Implementation::collision(abstractTransformedShape(), other.abstractTransformedShape()); +} + template void AbstractShape::markDirty() { if(group()) group()->setDirty(); } diff --git a/src/Magnum/Shapes/AbstractShape.h b/src/Magnum/Shapes/AbstractShape.h index 0d558308e..e613031cf 100644 --- a/src/Magnum/Shapes/AbstractShape.h +++ b/src/Magnum/Shapes/AbstractShape.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class Magnum::Shapes::AbstractShape, typedef Magnum::Shapes::AbstractShape2D, Magnum::Shapes::AbstractShape3D + * @brief Class @ref Magnum::Shapes::AbstractShape, typedef @ref Magnum::Shapes::AbstractShape2D, @ref Magnum::Shapes::AbstractShape3D */ #include "Magnum/Magnum.h" @@ -46,9 +46,9 @@ namespace Implementation { /** @brief Base class for object shapes -This class is not directly instantiable, see Shape instead. See @ref shapes for -brief introduction. -@see AbstractShape2D, AbstractShape3D +This class is not directly instantiable, use @ref Shape instead. See +@ref shapes for brief introduction. +@see @ref AbstractShape2D, @ref AbstractShape3D */ template class MAGNUM_SHAPES_EXPORT AbstractShape: public SceneGraph::AbstractGroupedFeature, Float> { /* MSVC can't cope with <> here */ @@ -99,9 +99,7 @@ template class MAGNUM_SHAPES_EXPORT AbstractShape: publi ShapeGroup* group(); const ShapeGroup* group() const; /**< @overload */ - /** - * @brief Shape type - */ + /** @brief Shape type */ Type type() const; /** @@ -111,6 +109,13 @@ template class MAGNUM_SHAPES_EXPORT AbstractShape: publi */ bool collides(const AbstractShape& other) const; + /** + * @brief Collision with other shape + * + * Default implementation returns empty collision. + */ + Collision collision(const AbstractShape& other) const; + protected: /** Marks also the group as dirty */ void markDirty() override; diff --git a/src/Magnum/Shapes/AxisAlignedBox.h b/src/Magnum/Shapes/AxisAlignedBox.h index 900af0669..bb2d1076e 100644 --- a/src/Magnum/Shapes/AxisAlignedBox.h +++ b/src/Magnum/Shapes/AxisAlignedBox.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class Magnum::Shapes::AxisAlignedBox, typedef Magnum::Shapes::AxisAlignedBox2D, Magnum::Shapes.:AxisAlignedBox3D + * @brief Class @ref Magnum::Shapes::AxisAlignedBox, typedef @ref Magnum::Shapes::AxisAlignedBox2D, @ref Magnum::Shapes.:AxisAlignedBox3D */ #include "Magnum/DimensionTraits.h" @@ -40,7 +40,7 @@ namespace Magnum { namespace Shapes { @brief Axis-aligned box See @ref shapes for brief introduction. -@see AxisAlignedBox2D, AxisAlignedBox3D +@see @ref AxisAlignedBox2D, @ref AxisAlignedBox3D @todo Assert for rotation */ template class MAGNUM_SHAPES_EXPORT AxisAlignedBox { diff --git a/src/Magnum/Shapes/Box.h b/src/Magnum/Shapes/Box.h index c83822700..e6b785cd6 100644 --- a/src/Magnum/Shapes/Box.h +++ b/src/Magnum/Shapes/Box.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class Magnum::Shapes::Box, typedef Magnum::Shapes::Box2D, Magnum::Shapes::Box3D + * @brief Class @ref Magnum::Shapes::Box, typedef @ref Magnum::Shapes::Box2D, @ref Magnum::Shapes::Box3D */ #include "Magnum/DimensionTraits.h" @@ -41,8 +41,8 @@ namespace Magnum { namespace Shapes { Unit-size means that half extents are equal to 1, equivalent to e.g. sphere radius. See @ref shapes for brief introduction. +@see @ref Box2D, @ref Box3D @todo Use quat + position + size instead? -@see Box2D, Box3D @todo Assert for skew */ template class MAGNUM_SHAPES_EXPORT Box { diff --git a/src/Magnum/Shapes/CMakeLists.txt b/src/Magnum/Shapes/CMakeLists.txt index 812b209f8..4c8669773 100644 --- a/src/Magnum/Shapes/CMakeLists.txt +++ b/src/Magnum/Shapes/CMakeLists.txt @@ -62,6 +62,7 @@ set(MagnumShapes_HEADERS visibility.h) add_library(MagnumShapes ${SHARED_OR_STATIC} ${MagnumShapes_SRCS}) +set_target_properties(MagnumShapes PROPERTIES DEBUG_POSTFIX "-d") if(BUILD_STATIC_PIC) # TODO: CMake 2.8.9 has this as POSITION_INDEPENDENT_CODE property set_target_properties(MagnumShapes PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") diff --git a/src/Magnum/Shapes/Capsule.h b/src/Magnum/Shapes/Capsule.h index 42556e019..e760327fc 100644 --- a/src/Magnum/Shapes/Capsule.h +++ b/src/Magnum/Shapes/Capsule.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class Magnum::Shapes::Capsule, typedef Magnum::Shapes::Capsule2D, Magnum::Shapes::Capsule3D + * @brief Class @ref Magnum::Shapes::Capsule, typedef @ref Magnum::Shapes::Capsule2D, @ref Magnum::Shapes::Capsule3D */ #include "Magnum/DimensionTraits.h" @@ -41,7 +41,7 @@ namespace Magnum { namespace Shapes { Unlike other elements the capsule expects uniform scaling. See @ref shapes for brief introduction. -@see Capsule2D, Capsule3D, Cylinder +@see @ref Capsule2D, @ref Capsule3D, @ref Cylinder @todo Store the radius as squared value to avoid sqrt/pow? Will complicate collision detection with sphere. */ diff --git a/src/Magnum/Shapes/Collision.h b/src/Magnum/Shapes/Collision.h index 0ee1528ff..74f2f9d88 100644 --- a/src/Magnum/Shapes/Collision.h +++ b/src/Magnum/Shapes/Collision.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Shapes::Collision + * @brief Class @ref Magnum::Shapes::Collision, typedef @ref Magnum::Shapes::Collision2D, @ref Magnum::Shapes::Collision3D */ #include "Magnum/DimensionTraits.h" diff --git a/src/Magnum/Shapes/Composition.h b/src/Magnum/Shapes/Composition.h index c0d78f42f..4263f824e 100644 --- a/src/Magnum/Shapes/Composition.h +++ b/src/Magnum/Shapes/Composition.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class Magnum::Shapes::Composition, enum Magnum::Shapes::CompositionOperation + * @brief Class @ref Magnum::Shapes::Composition, typedef @ref Magnum::Shapes::Composition2D, @ref Magnum::Shapes::Composition3D, enum @ref Magnum::Shapes::CompositionOperation */ #include @@ -101,7 +101,7 @@ template class MAGNUM_SHAPES_EXPORT Composition { /** * @brief Default constructor * - * Creates empty hierarchy. + * Creates empty composition. */ explicit Composition() {} @@ -193,10 +193,10 @@ template class MAGNUM_SHAPES_EXPORT Composition { Containers::Array _nodes; }; -/** @brief Two-dimensional shape hierarchy */ +/** @brief Two-dimensional shape composition */ typedef Composition<2> Composition2D; -/** @brief Three-dimensional shape hierarchy */ +/** @brief Three-dimensional shape composition */ typedef Composition<3> Composition3D; #ifdef DOXYGEN_GENERATING_OUTPUT @@ -205,7 +205,7 @@ template Debug operator<<(Debug debug, typename Composit #endif /** @relates Composition -@brief Collision of shape with Composition +@brief Collision of shape with @ref Composition */ #ifdef DOXYGEN_GENERATING_OUTPUT template inline bool operator%(const T& a, const Composition& b) { diff --git a/src/Magnum/Shapes/Cylinder.h b/src/Magnum/Shapes/Cylinder.h index 6730c8e0b..04a311a71 100644 --- a/src/Magnum/Shapes/Cylinder.h +++ b/src/Magnum/Shapes/Cylinder.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class Magnum::Shapes::Cylinder, typedef Magnum::Shapes::Cylinder2D, Magnum::Shapes::Cylinder3D + * @brief Class @ref Magnum::Shapes::Cylinder, typedef @ref Magnum::Shapes::Cylinder2D, @ref Magnum::Shapes::Cylinder3D */ #include "Magnum/DimensionTraits.h" diff --git a/src/Magnum/Shapes/Implementation/CollisionDispatch.cpp b/src/Magnum/Shapes/Implementation/CollisionDispatch.cpp index 11174398c..1213062d0 100644 --- a/src/Magnum/Shapes/Implementation/CollisionDispatch.cpp +++ b/src/Magnum/Shapes/Implementation/CollisionDispatch.cpp @@ -66,6 +66,21 @@ template<> bool collides(const AbstractShape<2>& a, const AbstractShape<2>& b) { return false; } +template<> Collision<2> collision(const AbstractShape<2>& a, const AbstractShape<2>& b) { + if(a.type() < b.type()) return collision(b, a); + + switch(UnsignedInt(a.type())*UnsignedInt(b.type())) { + #define _c(aType, aClass, bType, bClass) \ + case UnsignedInt(ShapeDimensionTraits<2>::Type::aType)*UnsignedInt(ShapeDimensionTraits<2>::Type::bType): \ + return static_cast&>(a).shape / static_cast&>(b).shape; + _c(Sphere, Sphere2D, Point, Point2D) + _c(Sphere, Sphere2D, Sphere, Sphere2D) + #undef _c + } + + return {}; +} + template<> bool collides(const AbstractShape<3>& a, const AbstractShape<3>& b) { /* GCC 4.4 doesn't have comparison operators for strongly typed enums */ if(UnsignedInt(a.type()) < UnsignedInt(b.type())) return collides(b, a); @@ -98,4 +113,19 @@ template<> bool collides(const AbstractShape<3>& a, const AbstractShape<3>& b) { return false; } +template<> Collision<3> collision(const AbstractShape<3>& a, const AbstractShape<3>& b) { + if(a.type() < b.type()) return collision(b, a); + + switch(UnsignedInt(a.type())*UnsignedInt(b.type())) { + #define _c(aType, aClass, bType, bClass) \ + case UnsignedInt(ShapeDimensionTraits<3>::Type::aType)*UnsignedInt(ShapeDimensionTraits<3>::Type::bType): \ + return static_cast&>(a).shape / static_cast&>(b).shape; + _c(Sphere, Sphere3D, Point, Point3D) + _c(Sphere, Sphere3D, Sphere, Sphere3D) + #undef _c + } + + return {}; +} + }}} diff --git a/src/Magnum/Shapes/Implementation/CollisionDispatch.h b/src/Magnum/Shapes/Implementation/CollisionDispatch.h index cd1dd564b..84d79aa2f 100644 --- a/src/Magnum/Shapes/Implementation/CollisionDispatch.h +++ b/src/Magnum/Shapes/Implementation/CollisionDispatch.h @@ -26,6 +26,7 @@ */ #include "Magnum/Types.h" +#include "Magnum/Shapes/Shapes.h" namespace Magnum { namespace Shapes { namespace Implementation { @@ -40,8 +41,11 @@ multiply the two numbers together and switch() on the result. Because of multiplying two prime numbers, there is no ambiguity (the result is unique for each combination). */ + template bool collides(const AbstractShape& a, const AbstractShape& b); +template Collision collision(const AbstractShape& a, const AbstractShape& b); + }}} #endif diff --git a/src/Magnum/Shapes/Line.h b/src/Magnum/Shapes/Line.h index da4d98bf8..553beec0c 100644 --- a/src/Magnum/Shapes/Line.h +++ b/src/Magnum/Shapes/Line.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class Magnum::Shapes::Line, typedef Magnum::Shapes::Line2D, Magnum::Shapes::Line3D + * @brief Class @ref Magnum::Shapes::Line, typedef @ref Magnum::Shapes::Line2D, @ref Magnum::Shapes::Line3D */ #include "Magnum/DimensionTraits.h" @@ -39,8 +39,8 @@ namespace Magnum { namespace Shapes { @brief Infinite line, defined by two points See @ref shapes for brief introduction. -@see Line2D, Line3D -@todo collision detection of two Line2D +@see @ref Line2D, @ref Line3D +@todo collision detection of two @ref Line2D */ template class MAGNUM_SHAPES_EXPORT Line { public: diff --git a/src/Magnum/Shapes/LineSegment.h b/src/Magnum/Shapes/LineSegment.h index 6a27e6712..da20b31b5 100644 --- a/src/Magnum/Shapes/LineSegment.h +++ b/src/Magnum/Shapes/LineSegment.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class Magnum::Shapes::LineSegment, typedef Magnum::Shapes::LineSegment2D, Magnum::Shapes::LineSegment3D + * @brief Class @ref Magnum::Shapes::LineSegment, typedef @ref Magnum::Shapes::LineSegment2D, @ref Magnum::Shapes::LineSegment3D */ #include "Magnum/Shapes/Line.h" @@ -37,7 +37,7 @@ namespace Magnum { namespace Shapes { @brief %Line segment, defined by starting and ending point See @ref shapes for brief introduction. -@see LineSegment2D, LineSegment3D +@see @ref LineSegment2D, @ref LineSegment3D */ template class LineSegment: public Line { public: diff --git a/src/Magnum/Shapes/Plane.h b/src/Magnum/Shapes/Plane.h index 8cac28180..70d6ec18c 100644 --- a/src/Magnum/Shapes/Plane.h +++ b/src/Magnum/Shapes/Plane.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class Magnum::Shapes::Plane + * @brief Class @ref Magnum::Shapes::Plane */ #include "Magnum/Magnum.h" diff --git a/src/Magnum/Shapes/Point.h b/src/Magnum/Shapes/Point.h index b0c6790f6..b2358ade3 100644 --- a/src/Magnum/Shapes/Point.h +++ b/src/Magnum/Shapes/Point.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class Magnum::Shapes::Point, typedef Magnum::Shapes::Point2D, Magnum::Shapes::Point3D + * @brief Class @ref Magnum::Shapes::Point, typedef @ref Magnum::Shapes::Point2D, @ref Magnum::Shapes::Point3D */ #include "Magnum/DimensionTraits.h" @@ -39,7 +39,7 @@ namespace Magnum { namespace Shapes { @brief %Point See @ref shapes for brief introduction. -@see Point2D, Point3D +@see @ref Point2D, @ref Point3D */ template class MAGNUM_SHAPES_EXPORT Point { public: diff --git a/src/Magnum/Shapes/Shape.h b/src/Magnum/Shapes/Shape.h index e1c70e8ed..dafa95226 100644 --- a/src/Magnum/Shapes/Shape.h +++ b/src/Magnum/Shapes/Shape.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class Magnum::Shapes::Shape + * @brief Class @ref Magnum::Shapes::Shape */ #include "Magnum/Shapes/AbstractShape.h" @@ -43,16 +43,17 @@ namespace Implementation { @brief Object shape Adds shape for collision detection to object. Each %Shape is part of -some ShapeGroup, which essentially maintains a set of objects which can +some @ref ShapeGroup, which essentially maintains a set of objects which can collide with each other. See @ref shapes for brief introduction. -The shape contains original shape with relative transformation under shape() -and also caches a shape with absolute transformation under transformedShape(), -which can be used for collision detection. To conveniently use collision -detection among many object, you need to add the shape to ShapeGroup, which -then provides collision detection for given group of shapes. You can also use -ShapeGroup::add() and ShapeGroup::remove() later to manage e.g. collision -islands. +The shape contains original shape with relative transformation under +@ref shape() and also caches a shape with absolute transformation under +@ref transformedShape(), which can be used for collision detection. To +conveniently use collision detection among many objects, you need to add the +shape to @ref ShapeGroup, which then provides collision detection for given +group of shapes using either @ref collides(), @ref collision() or +@ref ShapeGroup::firstCollision(). You can also use @ref ShapeGroup::add() and +@ref ShapeGroup::remove() later to manage e.g. collision islands. @code Shapes::ShapeGroup3D shapes; @@ -62,8 +63,8 @@ auto shape = new Shapes::Shape(object, {{}, 0.75f}, &shapes); Shapes::AbstractShape3D* firstCollision = shapes.firstCollision(shape); @endcode -@see @ref scenegraph, ShapeGroup2D, ShapeGroup3D, - DebugTools::ShapeRenderer +@see @ref scenegraph, @ref ShapeGroup2D, @ref ShapeGroup3D, + @ref DebugTools::ShapeRenderer */ template class Shape: public AbstractShape { friend struct Implementation::ShapeHelper; diff --git a/src/Magnum/Shapes/ShapeGroup.h b/src/Magnum/Shapes/ShapeGroup.h index 0b8295a4d..83ca485d9 100644 --- a/src/Magnum/Shapes/ShapeGroup.h +++ b/src/Magnum/Shapes/ShapeGroup.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class Magnum::Shapes::ShapeGroup, typedef Magnum::Shapes::ShapeGroup2D, Magnum::Shapes::ShapeGroup3D + * @brief Class @ref Magnum::Shapes::ShapeGroup, typedef @ref Magnum::Shapes::ShapeGroup2D, @ref Magnum::Shapes::ShapeGroup3D */ #include @@ -40,8 +40,8 @@ namespace Magnum { namespace Shapes { /** @brief Group of shapes -See Shape for more information. See @ref shapes for brief introduction. -@see @ref scenegraph, ShapeGroup2D, ShapeGroup3D +See @ref Shape for more information. See @ref shapes for brief introduction. +@see @ref scenegraph, @ref ShapeGroup2D, @ref ShapeGroup3D */ template class MAGNUM_SHAPES_EXPORT ShapeGroup: public SceneGraph::FeatureGroup, Float> { friend class AbstractShape; @@ -66,8 +66,7 @@ template class MAGNUM_SHAPES_EXPORT ShapeGroup: public S * If some body in the group changes its transformation, it sets dirty * status also on the group to indicate that the body and maybe also * group state needs to be cleaned before computing collisions. - * - * @see setClean() + * @see @ref setClean() */ void setDirty() { dirty = true; } @@ -83,7 +82,7 @@ template class MAGNUM_SHAPES_EXPORT ShapeGroup: public S * @brief First collision of given shape with other shapes in the group * * Returns first shape colliding with given one. If there aren't any - * collisions, returns `nullptr`. Calls setClean() before the + * collisions, returns `nullptr`. Calls @ref setClean() before the * operation. */ AbstractShape* firstCollision(const AbstractShape& shape); @@ -93,18 +92,18 @@ template class MAGNUM_SHAPES_EXPORT ShapeGroup: public S }; /** -@brief Group of two-dimensional shaped objects +@brief Group of two-dimensional shapes -See Shape for more information. -@see ShapeGroup3D +See @ref Shape for more information. +@see @ref ShapeGroup3D */ typedef ShapeGroup<2> ShapeGroup2D; /** -@brief Group of three-dimensional shaped objects +@brief Group of three-dimensional shapes -See Shape for more information. -@see ShapeGroup2D +See @ref Shape for more information. +@see @ref ShapeGroup2D */ typedef ShapeGroup<3> ShapeGroup3D; diff --git a/src/Magnum/Shapes/Shapes.h b/src/Magnum/Shapes/Shapes.h index 5773f0aac..0c5382b09 100644 --- a/src/Magnum/Shapes/Shapes.h +++ b/src/Magnum/Shapes/Shapes.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Forward declarations for Magnum::Shapes namespace + * @brief Forward declarations for @ref Magnum::Shapes namespace */ #include "Magnum/Types.h" diff --git a/src/Magnum/Shapes/Sphere.h b/src/Magnum/Shapes/Sphere.h index 42960ca66..0b02115d8 100644 --- a/src/Magnum/Shapes/Sphere.h +++ b/src/Magnum/Shapes/Sphere.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class Magnum::Shapes::Sphere, typedef Magnum::Shapes::Sphere2D, Magnum::Shapes::Sphere3D + * @brief Class @ref Magnum::Shapes::Sphere, typedef @ref Magnum::Shapes::Sphere2D, @ref Magnum::Shapes::Sphere3D */ #include "Magnum/DimensionTraits.h" @@ -42,7 +42,7 @@ namespace Magnum { namespace Shapes { Unlike other elements the sphere expects uniform scaling. See @ref shapes for brief introduction. -@see Sphere2D, Sphere3D +@see @ref Sphere2D, @ref Sphere3D @todo Store the radius as squared value to avoid sqrt/pow? Will complicate collision detection with another sphere. */ diff --git a/src/Magnum/Shapes/Test/ShapeTest.cpp b/src/Magnum/Shapes/Test/ShapeTest.cpp index 6f35197dd..fe5a50d55 100644 --- a/src/Magnum/Shapes/Test/ShapeTest.cpp +++ b/src/Magnum/Shapes/Test/ShapeTest.cpp @@ -41,6 +41,8 @@ class ShapeTest: public TestSuite::Tester { ShapeTest(); void clean(); + void collides(); + void collision(); void firstCollision(); void shapeGroup(); }; @@ -52,6 +54,8 @@ typedef SceneGraph::Object Object3D; ShapeTest::ShapeTest() { addTests({&ShapeTest::clean, + &ShapeTest::collides, + &ShapeTest::collision, &ShapeTest::firstCollision, &ShapeTest::shapeGroup}); } @@ -96,6 +100,88 @@ void ShapeTest::clean() { CORRADE_VERIFY(b.isDirty()); } +void ShapeTest::collides() { + Scene3D scene; + ShapeGroup3D shapes; + Object3D a(&scene); + Shape aShape(a, {{1.0f, -2.0f, 3.0f}, 1.5f}, &shapes); + + { + /* Collision with point inside the sphere */ + Shape aShape2(a, {{1.0f, -2.0f, 3.0f}}, &shapes); + shapes.setClean(); + CORRADE_VERIFY(aShape.collides(aShape2)); + } { + /* No collision with point inside the sphere, but not in the same group */ + ShapeGroup3D shapes2; + Shape aShape3(a, {{1.0f, -2.0f, 3.0f}}, &shapes2); + shapes2.setClean(); + CORRADE_VERIFY(!aShape.collides(aShape3)); + } { + CORRADE_EXPECT_FAIL("Should cross-scene collision work or not?"); + /* No collision with point inside the sphere, but not in the same scene */ + Scene3D scene2; + Object3D c(&scene2); + Shape cShape(c, {{1.0f, -2.0f, 3.0f}}, &shapes); + shapes.setClean(); + CORRADE_VERIFY(!aShape.collides(cShape)); + } { + /* No collision with point outside of the sphere */ + Object3D b(&scene); + Shape bShape(b, {{3.0f, -2.0f, 3.0f}}, &shapes); + shapes.setClean(); + CORRADE_VERIFY(!aShape.collides(bShape)); + + /* Move point inside the sphere -- collision */ + b.translate(Vector3::xAxis(-1.0f)); + shapes.setClean(); + CORRADE_VERIFY(aShape.collides(bShape)); + } +} + +void ShapeTest::collision() { + Scene3D scene; + ShapeGroup3D shapes; + Object3D a(&scene); + Shape aShape(a, {{1.0f, -2.0f, 3.0f}, 1.5f}, &shapes); + + { + /* Collision with point inside the sphere */ + Shape aShape2(a, {{1.0f, -2.0f, 3.0f}}, &shapes); + shapes.setClean(); + const Collision3D collision = aShape.collision(aShape2); + CORRADE_VERIFY(collision); + CORRADE_COMPARE(collision.position(), Vector3(1.0f, -2.0f, 3.0f)); + } { + /* No collision with point inside the sphere, but not in the same group */ + ShapeGroup3D shapes2; + Shape aShape3(a, {{1.0f, -2.0f, 3.0f}}, &shapes2); + shapes2.setClean(); + CORRADE_VERIFY(!aShape.collision(aShape3)); + } { + CORRADE_EXPECT_FAIL("Should cross-scene collision work or not?"); + /* No collision with point inside the sphere, but not in the same scene */ + Scene3D scene2; + Object3D c(&scene2); + Shape cShape(c, {{1.0f, -2.0f, 3.0f}}, &shapes); + shapes.setClean(); + CORRADE_VERIFY(!aShape.collision(cShape)); + } { + /* No collision with point outside of the sphere */ + Object3D b(&scene); + Shape bShape(b, {{3.0f, -2.0f, 3.0f}}, &shapes); + shapes.setClean(); + CORRADE_VERIFY(!aShape.collision(bShape)); + + /* Move point inside the sphere -- collision */ + b.translate(Vector3::xAxis(-1.0f)); + shapes.setClean(); + const Collision3D collision = aShape.collision(bShape); + CORRADE_VERIFY(collision); + CORRADE_COMPARE(collision.position(), Vector3(2.0f, -2.0f, 3.0f)); + } +} + void ShapeTest::firstCollision() { Scene3D scene; ShapeGroup3D shapes; @@ -106,9 +192,6 @@ void ShapeTest::firstCollision() { Object3D b(&scene); Shape bShape(b, {{3.0f, -2.0f, 3.0f}}, &shapes); - Object3D c(&scene); - Shape cShape(c, &shapes); - /* No collisions initially */ CORRADE_VERIFY(!shapes.firstCollision(aShape)); CORRADE_VERIFY(!shapes.firstCollision(bShape)); diff --git a/src/Magnum/Test/AbstractShaderProgramTest.cpp b/src/Magnum/Test/AbstractShaderProgramTest.cpp index 0344784c0..71e2425c9 100644 --- a/src/Magnum/Test/AbstractShaderProgramTest.cpp +++ b/src/Magnum/Test/AbstractShaderProgramTest.cpp @@ -76,6 +76,7 @@ AbstractShaderProgramTest::AbstractShaderProgramTest() { void AbstractShaderProgramTest::attributeScalar() { typedef AbstractShaderProgram::Attribute<3, Float> Attribute; + CORRADE_VERIFY((std::is_same{})); CORRADE_COMPARE(Int(Attribute::Location), 3); CORRADE_COMPARE(Int(Attribute::VectorCount), 1); @@ -95,6 +96,7 @@ void AbstractShaderProgramTest::attributeScalar() { void AbstractShaderProgramTest::attributeScalarInt() { #ifndef MAGNUM_TARGET_GLES2 typedef AbstractShaderProgram::Attribute<3, Int> Attribute; + CORRADE_VERIFY((std::is_same{})); CORRADE_COMPARE(Int(Attribute::VectorCount), 1); /* Default constructor */ @@ -112,6 +114,7 @@ void AbstractShaderProgramTest::attributeScalarInt() { void AbstractShaderProgramTest::attributeScalarUnsignedInt() { #ifndef MAGNUM_TARGET_GLES2 typedef AbstractShaderProgram::Attribute<3, UnsignedInt> Attribute; + CORRADE_VERIFY((std::is_same{})); CORRADE_COMPARE(Int(Attribute::VectorCount), 1); /* Default constructor */ @@ -129,6 +132,7 @@ void AbstractShaderProgramTest::attributeScalarUnsignedInt() { void AbstractShaderProgramTest::attributeScalarDouble() { #ifndef MAGNUM_TARGET_GLES typedef AbstractShaderProgram::Attribute<3, Double> Attribute; + CORRADE_VERIFY((std::is_same{})); CORRADE_COMPARE(Int(Attribute::VectorCount), 1); /* Default constructor */ @@ -141,6 +145,7 @@ void AbstractShaderProgramTest::attributeScalarDouble() { void AbstractShaderProgramTest::attributeVector() { typedef AbstractShaderProgram::Attribute<3, Vector3> Attribute; + CORRADE_VERIFY((std::is_same{})); CORRADE_COMPARE(Int(Attribute::VectorCount), 1); /* Default constructor */ @@ -164,6 +169,7 @@ void AbstractShaderProgramTest::attributeVector() { void AbstractShaderProgramTest::attributeVectorInt() { #ifndef MAGNUM_TARGET_GLES2 typedef AbstractShaderProgram::Attribute<3, Vector2i> Attribute; + CORRADE_VERIFY((std::is_same{})); CORRADE_COMPARE(Int(Attribute::VectorCount), 1); /* Default constructor */ @@ -183,6 +189,7 @@ void AbstractShaderProgramTest::attributeVectorInt() { void AbstractShaderProgramTest::attributeVectorUnsignedInt() { #ifndef MAGNUM_TARGET_GLES2 typedef AbstractShaderProgram::Attribute<3, Vector4ui> Attribute; + CORRADE_VERIFY((std::is_same{})); CORRADE_COMPARE(Int(Attribute::VectorCount), 1); /* Default constructor */ @@ -202,6 +209,7 @@ void AbstractShaderProgramTest::attributeVectorUnsignedInt() { void AbstractShaderProgramTest::attributeVectorDouble() { #ifndef MAGNUM_TARGET_GLES typedef AbstractShaderProgram::Attribute<3, Vector2d> Attribute; + CORRADE_VERIFY((std::is_same{})); CORRADE_COMPARE(Int(Attribute::VectorCount), 1); /* Default constructor */ @@ -220,6 +228,7 @@ void AbstractShaderProgramTest::attributeVectorDouble() { void AbstractShaderProgramTest::attributeVector4() { typedef AbstractShaderProgram::Attribute<3, Vector4> Attribute; + CORRADE_VERIFY((std::is_same{})); CORRADE_COMPARE(Int(Attribute::VectorCount), 1); /* Custom type */ @@ -235,6 +244,7 @@ void AbstractShaderProgramTest::attributeVector4() { void AbstractShaderProgramTest::attributeVectorBGRA() { #ifndef MAGNUM_TARGET_GLES typedef AbstractShaderProgram::Attribute<3, Vector4> Attribute; + CORRADE_VERIFY((std::is_same{})); CORRADE_COMPARE(Int(Attribute::VectorCount), 1); /* BGRA */ @@ -247,6 +257,7 @@ void AbstractShaderProgramTest::attributeVectorBGRA() { void AbstractShaderProgramTest::attributeMatrixNxN() { typedef AbstractShaderProgram::Attribute<3, Matrix3> Attribute; + CORRADE_VERIFY((std::is_same{})); CORRADE_COMPARE(Int(Attribute::VectorCount), 3); /* Default constructor */ @@ -259,6 +270,7 @@ void AbstractShaderProgramTest::attributeMatrixNxN() { #ifndef MAGNUM_TARGET_GLES2 void AbstractShaderProgramTest::attributeMatrixMxN() { typedef AbstractShaderProgram::Attribute<3, Matrix3x4> Attribute; + CORRADE_VERIFY((std::is_same{})); CORRADE_COMPARE(Int(Attribute::VectorCount), 3); /* Default constructor */ @@ -272,6 +284,7 @@ void AbstractShaderProgramTest::attributeMatrixMxN() { void AbstractShaderProgramTest::attributeMatrixNxNd() { #ifndef MAGNUM_TARGET_GLES typedef AbstractShaderProgram::Attribute<3, Matrix4d> Attribute; + CORRADE_VERIFY((std::is_same{})); CORRADE_COMPARE(Int(Attribute::VectorCount), 4); /* Default constructor */ @@ -287,6 +300,7 @@ void AbstractShaderProgramTest::attributeMatrixNxNd() { void AbstractShaderProgramTest::attributeMatrixMxNd() { #ifndef MAGNUM_TARGET_GLES typedef AbstractShaderProgram::Attribute<3, Matrix4x2d> Attribute; + CORRADE_VERIFY((std::is_same{})); CORRADE_COMPARE(Int(Attribute::VectorCount), 4); /* Default constructor */ diff --git a/src/Magnum/Test/BufferTextureGLTest.cpp b/src/Magnum/Test/BufferTextureGLTest.cpp index d0cbd7a4e..0789b2aaa 100644 --- a/src/Magnum/Test/BufferTextureGLTest.cpp +++ b/src/Magnum/Test/BufferTextureGLTest.cpp @@ -36,12 +36,14 @@ class BufferTextureGLTest: public AbstractOpenGLTester { explicit BufferTextureGLTest(); void construct(); + void bind(); void setBuffer(); void setBufferOffset(); }; BufferTextureGLTest::BufferTextureGLTest() { addTests({&BufferTextureGLTest::construct, + &BufferTextureGLTest::bind, &BufferTextureGLTest::setBuffer, &BufferTextureGLTest::setBufferOffset}); } @@ -60,6 +62,37 @@ void BufferTextureGLTest::construct() { MAGNUM_VERIFY_NO_ERROR(); } +void BufferTextureGLTest::bind() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::texture_buffer_object::string() + std::string(" is not supported.")); + + BufferTexture texture; + + if(Context::current()->isExtensionSupported()) { + CORRADE_EXPECT_FAIL("With ARB_multi_bind the texture must be associated with given target at least once before binding it"); + Buffer buffer; + constexpr UnsignedByte data[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + buffer.setData(data, BufferUsage::StaticDraw); + texture.setBuffer(BufferTextureFormat::R8UI, buffer); + CORRADE_VERIFY(false); + } + + texture.bind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::bind(7, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); +} + void BufferTextureGLTest::setBuffer() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_buffer_object::string() + std::string(" is not supported.")); diff --git a/src/Magnum/Test/CMakeLists.txt b/src/Magnum/Test/CMakeLists.txt index 76f3eafa3..eb8f51ff1 100644 --- a/src/Magnum/Test/CMakeLists.txt +++ b/src/Magnum/Test/CMakeLists.txt @@ -27,6 +27,7 @@ corrade_add_test(AbstractImageTest AbstractImageTest.cpp LIBRARIES Magnum) corrade_add_test(AbstractShaderProgramTest AbstractShaderProgramTest.cpp LIBRARIES Magnum) corrade_add_test(ArrayTest ArrayTest.cpp) corrade_add_test(ColorTest ColorTest.cpp LIBRARIES MagnumMathTestLib) +corrade_add_test(ContextTest ContextTest.cpp LIBRARIES Magnum) corrade_add_test(DebugMessageTest DebugMessageTest.cpp LIBRARIES Magnum) corrade_add_test(DefaultFramebufferTest DefaultFramebufferTest.cpp LIBRARIES Magnum) corrade_add_test(FramebufferTest FramebufferTest.cpp LIBRARIES Magnum) diff --git a/src/Magnum/Test/ColorTest.cpp b/src/Magnum/Test/ColorTest.cpp index b454e48b2..2c19e312f 100644 --- a/src/Magnum/Test/ColorTest.cpp +++ b/src/Magnum/Test/ColorTest.cpp @@ -304,7 +304,7 @@ void ColorTest::hsvAlpha() { void ColorTest::swizzleType() { constexpr Color3 origColor3; - constexpr BasicColor4 origColor4; + constexpr Color4ub origColor4; #if !defined(CORRADE_GCC45_COMPATIBILITY) && !defined(CORRADE_MSVC2013_COMPATIBILITY) constexpr @@ -320,7 +320,7 @@ void ColorTest::swizzleType() { const #endif auto b = Math::swizzle<'y', 'z', 'a'>(origColor4); - CORRADE_VERIFY((std::is_same>::value)); + CORRADE_VERIFY((std::is_same::value)); #if !defined(CORRADE_GCC45_COMPATIBILITY) && !defined(CORRADE_MSVC2013_COMPATIBILITY) constexpr @@ -336,7 +336,7 @@ void ColorTest::swizzleType() { const #endif auto d = Math::swizzle<'y', 'a', 'y', 'x'>(origColor4); - CORRADE_VERIFY((std::is_same>::value)); + CORRADE_VERIFY((std::is_same::value)); } void ColorTest::debug() { diff --git a/src/Magnum/Swizzle.h b/src/Magnum/Test/ContextTest.cpp similarity index 62% rename from src/Magnum/Swizzle.h rename to src/Magnum/Test/ContextTest.cpp index 74ba3ab3a..cb1b76327 100644 --- a/src/Magnum/Swizzle.h +++ b/src/Magnum/Test/ContextTest.cpp @@ -1,5 +1,3 @@ -#ifndef Magnum_Swizzle_h -#define Magnum_Swizzle_h /* This file is part of Magnum. @@ -25,32 +23,30 @@ DEALINGS IN THE SOFTWARE. */ -#ifdef MAGNUM_BUILD_DEPRECATED -/** @file - * @brief Function @ref Magnum::swizzle() - * @deprecated Use @ref Math/Swizzle.h instead. - */ -#endif +#include +#include -#include "Magnum/Math/Swizzle.h" -#include "Magnum/Color.h" +#include "Magnum/Context.h" -#ifdef MAGNUM_BUILD_DEPRECATED -namespace Magnum { +namespace Magnum { namespace Test { -/** -@copybrief Math::swizzle() -@deprecated Use @ref Magnum::Math::swizzle() "Math::swizzle()" instead. -*/ -#ifdef DOXYGEN_GENERATING_OUTPUT -template constexpr CORRADE_DEPRECATED("use Math::swizzle() instead") typename Math::Implementation::TypeForSize::Type swizzle(const T& vector); -#else -using Math::swizzle; -#endif +class ContextTest: public TestSuite::Tester { + public: + explicit ContextTest(); + + void debugFlag(); +}; +ContextTest::ContextTest() { + addTests({&ContextTest::debugFlag}); } -#else -#error this header is available only on deprecated build -#endif -#endif +void ContextTest::debugFlag() { + std::ostringstream out; + Debug(&out) << Context::Flag::Debug; + CORRADE_COMPARE(out.str(), "Context::Flag::Debug\n"); +} + +}} + +CORRADE_TEST_MAIN(Magnum::Test::ContextTest) diff --git a/src/Magnum/Test/CubeMapTextureArrayGLTest.cpp b/src/Magnum/Test/CubeMapTextureArrayGLTest.cpp index e3da6a916..e6f9fdf63 100644 --- a/src/Magnum/Test/CubeMapTextureArrayGLTest.cpp +++ b/src/Magnum/Test/CubeMapTextureArrayGLTest.cpp @@ -40,26 +40,40 @@ class CubeMapTextureArrayGLTest: public AbstractOpenGLTester { explicit CubeMapTextureArrayGLTest(); void construct(); + void bind(); + void sampling(); + void samplingBorderInteger(); + void storage(); + void image(); void imageBuffer(); void subImage(); void subImageBuffer(); + void generateMipmap(); + void invalidateImage(); void invalidateSubImage(); }; CubeMapTextureArrayGLTest::CubeMapTextureArrayGLTest() { addTests({&CubeMapTextureArrayGLTest::construct, + &CubeMapTextureArrayGLTest::bind, + &CubeMapTextureArrayGLTest::sampling, + &CubeMapTextureArrayGLTest::samplingBorderInteger, + &CubeMapTextureArrayGLTest::storage, + &CubeMapTextureArrayGLTest::image, &CubeMapTextureArrayGLTest::imageBuffer, &CubeMapTextureArrayGLTest::subImage, &CubeMapTextureArrayGLTest::subImageBuffer, + &CubeMapTextureArrayGLTest::generateMipmap, + &CubeMapTextureArrayGLTest::invalidateImage, &CubeMapTextureArrayGLTest::invalidateSubImage}); } @@ -78,6 +92,30 @@ void CubeMapTextureArrayGLTest::construct() { MAGNUM_VERIFY_NO_ERROR(); } +void CubeMapTextureArrayGLTest::bind() { + CubeMapTextureArray texture; + + #ifndef MAGNUM_TARGET_GLES + if(Context::current()->isExtensionSupported()) { + CORRADE_EXPECT_FAIL("With ARB_multi_bind the texture must be associated with given target at least once before binding it."); + texture.setBaseLevel(0); + CORRADE_VERIFY(false); + } + #endif + + texture.bind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::bind(7, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); +} + void CubeMapTextureArrayGLTest::sampling() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_cube_map_array::string() + std::string(" is not supported.")); @@ -85,6 +123,8 @@ void CubeMapTextureArrayGLTest::sampling() { CubeMapTextureArray texture; texture.setMinificationFilter(Sampler::Filter::Linear, Sampler::Mipmap::Linear) .setMagnificationFilter(Sampler::Filter::Linear) + .setBaseLevel(1) + .setMaxLevel(750) .setWrapping(Sampler::Wrapping::ClampToBorder) .setBorderColor(Color3(0.5f)) .setMaxAnisotropy(Sampler::maxMaxAnisotropy()); @@ -92,6 +132,20 @@ void CubeMapTextureArrayGLTest::sampling() { MAGNUM_VERIFY_NO_ERROR(); } +void CubeMapTextureArrayGLTest::samplingBorderInteger() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::texture_integer::string() + std::string(" is not supported.")); + + CubeMapTextureArray a; + a.setWrapping(Sampler::Wrapping::ClampToBorder) + .setBorderColor(Vector4i(1, 56, 78, -2)); + CubeMapTextureArray b; + b.setWrapping(Sampler::Wrapping::ClampToBorder) + .setBorderColor(Vector4ui(35, 56, 78, 15)); + + MAGNUM_VERIFY_NO_ERROR(); +} + void CubeMapTextureArrayGLTest::storage() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_cube_map_array::string() + std::string(" is not supported.")); diff --git a/src/Magnum/Test/CubeMapTextureGLTest.cpp b/src/Magnum/Test/CubeMapTextureGLTest.cpp index 6e4d75a77..c820d365c 100644 --- a/src/Magnum/Test/CubeMapTextureGLTest.cpp +++ b/src/Magnum/Test/CubeMapTextureGLTest.cpp @@ -43,34 +43,60 @@ class CubeMapTextureGLTest: public AbstractOpenGLTester { explicit CubeMapTextureGLTest(); void construct(); + void bind(); + void sampling(); + #ifdef MAGNUM_TARGET_GLES2 + void samplingMaxLevel(); + #endif + #ifndef MAGNUM_TARGET_GLES + void samplingBorderInteger(); + #endif + void storage(); + void image(); #ifndef MAGNUM_TARGET_GLES2 void imageBuffer(); #endif + void subImage(); #ifndef MAGNUM_TARGET_GLES2 void subImageBuffer(); #endif + void generateMipmap(); + void invalidateImage(); void invalidateSubImage(); }; CubeMapTextureGLTest::CubeMapTextureGLTest() { addTests({&CubeMapTextureGLTest::construct, + &CubeMapTextureGLTest::bind, + &CubeMapTextureGLTest::sampling, + #ifdef MAGNUM_TARGET_GLES2 + &CubeMapTextureGLTest::samplingMaxLevel, + #endif + #ifndef MAGNUM_TARGET_GLES + &CubeMapTextureGLTest::samplingBorderInteger, + #endif + &CubeMapTextureGLTest::storage, + &CubeMapTextureGLTest::image, #ifndef MAGNUM_TARGET_GLES2 &CubeMapTextureGLTest::imageBuffer, #endif + &CubeMapTextureGLTest::subImage, #ifndef MAGNUM_TARGET_GLES2 &CubeMapTextureGLTest::subImageBuffer, #endif + &CubeMapTextureGLTest::generateMipmap, + &CubeMapTextureGLTest::invalidateImage, &CubeMapTextureGLTest::invalidateSubImage}); } @@ -86,10 +112,38 @@ void CubeMapTextureGLTest::construct() { MAGNUM_VERIFY_NO_ERROR(); } +void CubeMapTextureGLTest::bind() { + CubeMapTexture texture; + + #ifndef MAGNUM_TARGET_GLES + if(Context::current()->isExtensionSupported()) { + CORRADE_EXPECT_FAIL("With ARB_multi_bind the texture must be associated with given target at least once before binding it."); + texture.setBaseLevel(0); + CORRADE_VERIFY(false); + } + #endif + + texture.bind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::bind(7, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); +} + void CubeMapTextureGLTest::sampling() { CubeMapTexture texture; texture.setMinificationFilter(Sampler::Filter::Linear, Sampler::Mipmap::Linear) .setMagnificationFilter(Sampler::Filter::Linear) + #ifndef MAGNUM_TARGET_GLES2 + .setBaseLevel(1) + .setMaxLevel(750) + #endif .setWrapping(Sampler::Wrapping::ClampToBorder) .setBorderColor(Color3(0.5f)) .setMaxAnisotropy(Sampler::maxMaxAnisotropy()); @@ -97,6 +151,34 @@ void CubeMapTextureGLTest::sampling() { MAGNUM_VERIFY_NO_ERROR(); } +#ifdef MAGNUM_TARGET_GLES2 +void CubeMapTextureGLTest::samplingMaxLevel() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::APPLE::texture_max_level::string() + std::string(" is not supported.")); + + CubeMapTexture texture; + texture.setMaxLevel(750); + + MAGNUM_VERIFY_NO_ERROR(); +} +#endif + +#ifndef MAGNUM_TARGET_GLES +void CubeMapTextureGLTest::samplingBorderInteger() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::texture_integer::string() + std::string(" is not supported.")); + + CubeMapTexture a; + a.setWrapping(Sampler::Wrapping::ClampToBorder) + .setBorderColor(Vector4i(1, 56, 78, -2)); + CubeMapTexture b; + b.setWrapping(Sampler::Wrapping::ClampToBorder) + .setBorderColor(Vector4ui(35, 56, 78, 15)); + + MAGNUM_VERIFY_NO_ERROR(); +} +#endif + void CubeMapTextureGLTest::storage() { CubeMapTexture texture; texture.setStorage(5, TextureFormat::RGBA8, Vector2i(32)); diff --git a/src/Magnum/Test/MultisampleTextureGLTest.cpp b/src/Magnum/Test/MultisampleTextureGLTest.cpp index 6c5e959cc..e5b8115b4 100644 --- a/src/Magnum/Test/MultisampleTextureGLTest.cpp +++ b/src/Magnum/Test/MultisampleTextureGLTest.cpp @@ -26,6 +26,8 @@ #include #include "Magnum/MultisampleTexture.h" +#include "Magnum/TextureFormat.h" +#include "Magnum/Math/Vector3.h" #include "Magnum/Test/AbstractOpenGLTester.h" namespace Magnum { namespace Test { @@ -37,19 +39,12 @@ class MultisampleTextureGLTest: public AbstractOpenGLTester { void construct2D(); void construct2DArray(); + void bind2D(); + void bind2DArray(); + void storage2D(); void storage2DArray(); - void image2D(); - void image2DBuffer(); - void image2DArray(); - void image2DArrayBuffer(); - - void subImage2D(); - void subImage2DBuffer(); - void subImage2DArray(); - void subImage2DArrayBuffer(); - void invalidateImage2D(); void invalidateImage2DArray(); @@ -61,19 +56,12 @@ MultisampleTextureGLTest::MultisampleTextureGLTest() { addTests({&MultisampleTextureGLTest::construct2D, &MultisampleTextureGLTest::construct2DArray, + &MultisampleTextureGLTest::bind2D, + &MultisampleTextureGLTest::bind2DArray, + &MultisampleTextureGLTest::storage2D, &MultisampleTextureGLTest::storage2DArray, - &MultisampleTextureGLTest::image2D, - &MultisampleTextureGLTest::image2DBuffer, - &MultisampleTextureGLTest::image2DArray, - &MultisampleTextureGLTest::image2DArrayBuffer, - - &MultisampleTextureGLTest::subImage2D, - &MultisampleTextureGLTest::subImage2DBuffer, - &MultisampleTextureGLTest::subImage2DArray, - &MultisampleTextureGLTest::subImage2DArrayBuffer, - &MultisampleTextureGLTest::invalidateImage2D, &MultisampleTextureGLTest::invalidateImage2DArray, @@ -109,102 +97,126 @@ void MultisampleTextureGLTest::construct2DArray() { MAGNUM_VERIFY_NO_ERROR(); } -void MultisampleTextureGLTest::storage2D() { +void MultisampleTextureGLTest::bind2D() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); - CORRADE_SKIP("Not implemented yet."); -} + MultisampleTexture2D texture; -void MultisampleTextureGLTest::storage2DArray() { - if(!Context::current()->isExtensionSupported()) - CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); + if(Context::current()->isExtensionSupported()) { + CORRADE_EXPECT_FAIL("With ARB_multi_bind the texture must be associated with given target at least once before binding it."); + texture.setStorage(4, TextureFormat::RGBA8, {16, 16}); + CORRADE_VERIFY(false); + } - CORRADE_SKIP("Not implemented yet."); -} + texture.bind(15); -void MultisampleTextureGLTest::image2D() { - if(!Context::current()->isExtensionSupported()) - CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); + MAGNUM_VERIFY_NO_ERROR(); - CORRADE_SKIP("Not implemented yet."); -} + AbstractTexture::unbind(15); -void MultisampleTextureGLTest::image2DBuffer() { - if(!Context::current()->isExtensionSupported()) - CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::bind(7, {&texture, nullptr, &texture}); - CORRADE_SKIP("Not implemented yet."); + MAGNUM_VERIFY_NO_ERROR(); } -void MultisampleTextureGLTest::image2DArray() { +void MultisampleTextureGLTest::bind2DArray() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); - CORRADE_SKIP("Not implemented yet."); -} + MultisampleTexture2DArray texture; -void MultisampleTextureGLTest::image2DArrayBuffer() { - if(!Context::current()->isExtensionSupported()) - CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); + if(Context::current()->isExtensionSupported()) { + CORRADE_EXPECT_FAIL("With ARB_multi_bind the texture must be associated with given target at least once before binding it."); + texture.setStorage(4, TextureFormat::RGBA8, {16, 16, 5}); + CORRADE_VERIFY(false); + } - CORRADE_SKIP("Not implemented yet."); -} + texture.bind(15); -void MultisampleTextureGLTest::subImage2D() { - if(!Context::current()->isExtensionSupported()) - CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); + MAGNUM_VERIFY_NO_ERROR(); - CORRADE_SKIP("Not implemented yet."); -} + AbstractTexture::unbind(15); -void MultisampleTextureGLTest::subImage2DBuffer() { - if(!Context::current()->isExtensionSupported()) - CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::bind(7, {&texture, nullptr, &texture}); - CORRADE_SKIP("Not implemented yet."); + MAGNUM_VERIFY_NO_ERROR(); } -void MultisampleTextureGLTest::subImage2DArray() { +void MultisampleTextureGLTest::storage2D() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); - CORRADE_SKIP("Not implemented yet."); + MultisampleTexture2D texture; + texture.setStorage(4, TextureFormat::RGBA8, {16, 16}); + + MAGNUM_VERIFY_NO_ERROR(); + + CORRADE_COMPARE(texture.imageSize(), Vector2i(16, 16)); + + MAGNUM_VERIFY_NO_ERROR(); } -void MultisampleTextureGLTest::subImage2DArrayBuffer() { +void MultisampleTextureGLTest::storage2DArray() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); - CORRADE_SKIP("Not implemented yet."); + MultisampleTexture2DArray texture; + texture.setStorage(4, TextureFormat::RGBA8, {16, 16, 5}); + + MAGNUM_VERIFY_NO_ERROR(); + + CORRADE_COMPARE(texture.imageSize(), Vector3i(16, 16, 5)); + + MAGNUM_VERIFY_NO_ERROR(); } void MultisampleTextureGLTest::invalidateImage2D() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); - CORRADE_SKIP("Multisample storage is not implemented yet."); + MultisampleTexture2D texture; + texture.setStorage(4, TextureFormat::RGBA8, {16, 16}); + texture.invalidateImage(); + + MAGNUM_VERIFY_NO_ERROR(); } void MultisampleTextureGLTest::invalidateImage2DArray() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); - CORRADE_SKIP("Multisample storage is not implemented yet."); + MultisampleTexture2DArray texture; + texture.setStorage(4, TextureFormat::RGBA8, {16, 16, 5}); + texture.invalidateImage(); + + MAGNUM_VERIFY_NO_ERROR(); } void MultisampleTextureGLTest::invalidateSubImage2D() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); - CORRADE_SKIP("Multisample storage is not implemented yet."); + MultisampleTexture2D texture; + texture.setStorage(4, TextureFormat::RGBA8, {16, 16}); + texture.invalidateSubImage({3, 4}, {5, 6}); + + MAGNUM_VERIFY_NO_ERROR(); } void MultisampleTextureGLTest::invalidateSubImage2DArray() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_multisample::string() + std::string(" is not supported.")); - CORRADE_SKIP("Multisample storage is not implemented yet."); + MultisampleTexture2DArray texture; + texture.setStorage(4, TextureFormat::RGBA8, {16, 16, 5}); + texture.invalidateSubImage({3, 4, 1}, {5, 6, 3}); + + MAGNUM_VERIFY_NO_ERROR(); } }} diff --git a/src/Magnum/Test/RectangleTextureGLTest.cpp b/src/Magnum/Test/RectangleTextureGLTest.cpp index e4ff02c4e..91404f694 100644 --- a/src/Magnum/Test/RectangleTextureGLTest.cpp +++ b/src/Magnum/Test/RectangleTextureGLTest.cpp @@ -36,17 +36,20 @@ namespace Magnum { namespace Test { -class TextureGLTest: public AbstractOpenGLTester { +class RectangleTextureGLTest: public AbstractOpenGLTester { public: - explicit TextureGLTest(); + explicit RectangleTextureGLTest(); void construct(); + void bind(); + void sampling(); + void samplingBorderInteger(); + void storage(); void image(); void imageBuffer(); - void subImage(); void subImageBuffer(); @@ -54,22 +57,26 @@ class TextureGLTest: public AbstractOpenGLTester { void invalidateSubImage(); }; -TextureGLTest::TextureGLTest() { - addTests({&TextureGLTest::construct, - &TextureGLTest::sampling, - &TextureGLTest::storage, +RectangleTextureGLTest::RectangleTextureGLTest() { + addTests({&RectangleTextureGLTest::construct, + &RectangleTextureGLTest::bind, + + &RectangleTextureGLTest::sampling, + &RectangleTextureGLTest::samplingBorderInteger, - &TextureGLTest::image, - &TextureGLTest::imageBuffer, + &RectangleTextureGLTest::storage, - &TextureGLTest::subImage, - &TextureGLTest::subImageBuffer, + &RectangleTextureGLTest::image, + &RectangleTextureGLTest::imageBuffer, - &TextureGLTest::invalidateImage, - &TextureGLTest::invalidateSubImage}); + &RectangleTextureGLTest::subImage, + &RectangleTextureGLTest::subImageBuffer, + + &RectangleTextureGLTest::invalidateImage, + &RectangleTextureGLTest::invalidateSubImage}); } -void TextureGLTest::construct() { +void RectangleTextureGLTest::construct() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_rectangle::string() + std::string(" is not supported.")); @@ -83,7 +90,34 @@ void TextureGLTest::construct() { MAGNUM_VERIFY_NO_ERROR(); } -void TextureGLTest::sampling() { +void RectangleTextureGLTest::bind() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::texture_rectangle::string() + std::string(" is not supported.")); + + RectangleTexture texture; + + #ifndef MAGNUM_TARGET_GLES + if(Context::current()->isExtensionSupported()) { + CORRADE_EXPECT_FAIL("With ARB_multi_bind the texture must be associated with given target at least once before binding it."); + texture.setWrapping(Sampler::Wrapping::ClampToEdge); + CORRADE_VERIFY(false); + } + #endif + + texture.bind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::bind(7, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); +} + +void RectangleTextureGLTest::sampling() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_rectangle::string() + std::string(" is not supported.")); @@ -97,7 +131,21 @@ void TextureGLTest::sampling() { MAGNUM_VERIFY_NO_ERROR(); } -void TextureGLTest::storage() { +void RectangleTextureGLTest::samplingBorderInteger() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::texture_integer::string() + std::string(" is not supported.")); + + RectangleTexture a; + a.setWrapping(Sampler::Wrapping::ClampToBorder) + .setBorderColor(Vector4i(1, 56, 78, -2)); + RectangleTexture b; + b.setWrapping(Sampler::Wrapping::ClampToBorder) + .setBorderColor(Vector4ui(35, 56, 78, 15)); + + MAGNUM_VERIFY_NO_ERROR(); +} + +void RectangleTextureGLTest::storage() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_rectangle::string() + std::string(" is not supported.")); @@ -111,7 +159,7 @@ void TextureGLTest::storage() { MAGNUM_VERIFY_NO_ERROR(); } -void TextureGLTest::image() { +void RectangleTextureGLTest::image() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_rectangle::string() + std::string(" is not supported.")); @@ -135,7 +183,7 @@ void TextureGLTest::image() { std::vector(data, data + 16), TestSuite::Compare::Container); } -void TextureGLTest::imageBuffer() { +void RectangleTextureGLTest::imageBuffer() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_rectangle::string() + std::string(" is not supported.")); @@ -160,7 +208,7 @@ void TextureGLTest::imageBuffer() { std::vector(data, data+16), TestSuite::Compare::Container); } -void TextureGLTest::subImage() { +void RectangleTextureGLTest::subImage() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_rectangle::string() + std::string(" is not supported.")); @@ -191,7 +239,7 @@ void TextureGLTest::subImage() { }), TestSuite::Compare::Container); } -void TextureGLTest::subImageBuffer() { +void RectangleTextureGLTest::subImageBuffer() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_rectangle::string() + std::string(" is not supported.")); @@ -223,7 +271,7 @@ void TextureGLTest::subImageBuffer() { }), TestSuite::Compare::Container); } -void TextureGLTest::invalidateImage() { +void RectangleTextureGLTest::invalidateImage() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_rectangle::string() + std::string(" is not supported.")); @@ -234,7 +282,7 @@ void TextureGLTest::invalidateImage() { MAGNUM_VERIFY_NO_ERROR(); } -void TextureGLTest::invalidateSubImage() { +void RectangleTextureGLTest::invalidateSubImage() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::texture_rectangle::string() + std::string(" is not supported.")); @@ -247,4 +295,4 @@ void TextureGLTest::invalidateSubImage() { }} -CORRADE_TEST_MAIN(Magnum::Test::TextureGLTest) +CORRADE_TEST_MAIN(Magnum::Test::RectangleTextureGLTest) diff --git a/src/Magnum/Test/ShaderGLTest.cpp b/src/Magnum/Test/ShaderGLTest.cpp index 447253fe2..30e410b6e 100644 --- a/src/Magnum/Test/ShaderGLTest.cpp +++ b/src/Magnum/Test/ShaderGLTest.cpp @@ -77,7 +77,7 @@ void ShaderGLTest::construct() { #ifndef MAGNUM_TARGET_GLES CORRADE_COMPARE(shader.sources(), std::vector{"#version 130\n"}); #else - CORRADE_COMPARE(shader.sources(), std::vector{"#version 300\n"}); + CORRADE_COMPARE(shader.sources(), std::vector{"#version 300 es\n"}); #endif } @@ -120,7 +120,7 @@ void ShaderGLTest::constructMove() { #ifndef MAGNUM_TARGET_GLES CORRADE_COMPARE(b.sources(), std::vector{"#version 130\n"}); #else - CORRADE_COMPARE(b.sources(), std::vector{"#version 300\n"}); + CORRADE_COMPARE(b.sources(), std::vector{"#version 300 es\n"}); #endif #ifndef MAGNUM_TARGET_GLES @@ -139,7 +139,7 @@ void ShaderGLTest::constructMove() { #ifndef MAGNUM_TARGET_GLES CORRADE_COMPARE(c.sources(), std::vector{"#version 130\n"}); #else - CORRADE_COMPARE(c.sources(), std::vector{"#version 300\n"}); + CORRADE_COMPARE(c.sources(), std::vector{"#version 300 es\n"}); #endif } diff --git a/src/Magnum/Test/TextureArrayGLTest.cpp b/src/Magnum/Test/TextureArrayGLTest.cpp index 2c2eccfde..d7c2d2f68 100644 --- a/src/Magnum/Test/TextureArrayGLTest.cpp +++ b/src/Magnum/Test/TextureArrayGLTest.cpp @@ -36,115 +36,139 @@ namespace Magnum { namespace Test { -class TextureGLTest: public AbstractOpenGLTester { +class TextureArrayGLTest: public AbstractOpenGLTester { public: - explicit TextureGLTest(); + explicit TextureArrayGLTest(); #ifndef MAGNUM_TARGET_GLES - void construct1DArray(); + void construct1D(); #endif - void construct2DArray(); + void construct2D(); #ifndef MAGNUM_TARGET_GLES - void sampling1DArray(); + void bind1D(); #endif - void sampling2DArray(); + void bind2D(); - #ifdef MAGNUM_TARGET_GLES - void samplingBorder2DArray(); + #ifndef MAGNUM_TARGET_GLES + void sampling1D(); + #endif + void sampling2D(); + + #ifdef MAGNUM_TARGET_GLES2 + void samplingMaxLevel2D(); #endif #ifndef MAGNUM_TARGET_GLES - void storage1DArray(); + void samplingBorderInteger1D(); + void samplingBorderInteger2D(); + #else + void samplingBorder2D(); #endif - void storage2DArray(); #ifndef MAGNUM_TARGET_GLES - void image1DArray(); - void image1DArrayBuffer(); + void storage1D(); + #endif + void storage2D(); + + #ifndef MAGNUM_TARGET_GLES + void image1D(); + void image1DBuffer(); #endif #ifndef MAGNUM_TARGET_GLES2 - void image2DArray(); - void image2DArrayBuffer(); + void image2D(); + void image2DBuffer(); #endif #ifndef MAGNUM_TARGET_GLES - void subImage1DArray(); - void subImage1DArrayBuffer(); + void subImage1D(); + void subImage1DBuffer(); #endif - void subImage2DArray(); - void subImage2DArrayBuffer(); + void subImage2D(); + void subImage2DBuffer(); #ifndef MAGNUM_TARGET_GLES - void generateMipmap1DArray(); + void generateMipmap1D(); #endif - void generateMipmap2DArray(); + void generateMipmap2D(); #ifndef MAGNUM_TARGET_GLES - void invalidateImage1DArray(); + void invalidateImage1D(); #endif - void invalidateImage2DArray(); + void invalidateImage2D(); #ifndef MAGNUM_TARGET_GLES - void invalidateSubImage1DArray(); + void invalidateSubImage1D(); #endif - void invalidateSubImage2DArray(); + void invalidateSubImage2D(); }; -TextureGLTest::TextureGLTest() { +TextureArrayGLTest::TextureArrayGLTest() { addTests({ #ifndef MAGNUM_TARGET_GLES - &TextureGLTest::construct1DArray, + &TextureArrayGLTest::construct1D, + #endif + &TextureArrayGLTest::construct2D, + + #ifndef MAGNUM_TARGET_GLES + &TextureArrayGLTest::bind1D, #endif - &TextureGLTest::construct2DArray, + &TextureArrayGLTest::bind2D, #ifndef MAGNUM_TARGET_GLES - &TextureGLTest::sampling1DArray, + &TextureArrayGLTest::sampling1D, #endif - &TextureGLTest::sampling2DArray, + &TextureArrayGLTest::sampling2D, - #ifdef MAGNUM_TARGET_GLES - &TextureGLTest::samplingBorder2DArray, + #ifdef MAGNUM_TARGET_GLES2 + &TextureArrayGLTest::samplingMaxLevel2D, #endif #ifndef MAGNUM_TARGET_GLES - &TextureGLTest::storage1DArray, + &TextureArrayGLTest::samplingBorderInteger1D, + &TextureArrayGLTest::samplingBorderInteger2D, + #else + &TextureArrayGLTest::samplingBorder2D, #endif - &TextureGLTest::storage2DArray, #ifndef MAGNUM_TARGET_GLES - &TextureGLTest::image1DArray, - &TextureGLTest::image1DArrayBuffer, + &TextureArrayGLTest::storage1D, #endif - &TextureGLTest::image2DArray, - &TextureGLTest::image2DArrayBuffer, + &TextureArrayGLTest::storage2D, #ifndef MAGNUM_TARGET_GLES - &TextureGLTest::subImage1DArray, - &TextureGLTest::subImage1DArrayBuffer, + &TextureArrayGLTest::image1D, + &TextureArrayGLTest::image1DBuffer, #endif - &TextureGLTest::subImage2DArray, - &TextureGLTest::subImage2DArrayBuffer, + &TextureArrayGLTest::image2D, + &TextureArrayGLTest::image2DBuffer, #ifndef MAGNUM_TARGET_GLES - &TextureGLTest::generateMipmap1DArray, + &TextureArrayGLTest::subImage1D, + &TextureArrayGLTest::subImage1DBuffer, #endif - &TextureGLTest::generateMipmap2DArray, + &TextureArrayGLTest::subImage2D, + &TextureArrayGLTest::subImage2DBuffer, #ifndef MAGNUM_TARGET_GLES - &TextureGLTest::invalidateImage1DArray, + &TextureArrayGLTest::generateMipmap1D, #endif - &TextureGLTest::invalidateImage2DArray, + &TextureArrayGLTest::generateMipmap2D, #ifndef MAGNUM_TARGET_GLES - &TextureGLTest::invalidateSubImage1DArray, + &TextureArrayGLTest::invalidateImage1D, #endif - &TextureGLTest::invalidateSubImage2DArray + &TextureArrayGLTest::invalidateImage2D, + + #ifndef MAGNUM_TARGET_GLES + &TextureArrayGLTest::invalidateSubImage1D, + #endif + &TextureArrayGLTest::invalidateSubImage2D }); } #ifndef MAGNUM_TARGET_GLES -void TextureGLTest::construct1DArray() { +void TextureArrayGLTest::construct1D() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -159,7 +183,7 @@ void TextureGLTest::construct1DArray() { } #endif -void TextureGLTest::construct2DArray() { +void TextureArrayGLTest::construct2D() { #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -176,22 +200,94 @@ void TextureGLTest::construct2DArray() { } #ifndef MAGNUM_TARGET_GLES -void TextureGLTest::sampling1DArray() { +void TextureArrayGLTest::bind1D() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); + + Texture1DArray texture; + + if(Context::current()->isExtensionSupported()) { + CORRADE_EXPECT_FAIL("With ARB_multi_bind the texture must be associated with given target at least once before binding it."); + texture.setBaseLevel(0); + CORRADE_VERIFY(false); + } + + texture.bind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::bind(7, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); +} +#endif + +void TextureArrayGLTest::bind2D() { + #ifndef MAGNUM_TARGET_GLES + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); + #endif + + Texture2DArray texture; + + #ifndef MAGNUM_TARGET_GLES + if(Context::current()->isExtensionSupported()) { + CORRADE_EXPECT_FAIL("With ARB_multi_bind the texture must be associated with given target at least once before binding it."); + texture.setBaseLevel(0); + CORRADE_VERIFY(false); + } + #endif + + texture.bind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::bind(7, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); +} + +#ifndef MAGNUM_TARGET_GLES +void TextureArrayGLTest::sampling1D() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); Texture1DArray texture; texture.setMinificationFilter(Sampler::Filter::Linear, Sampler::Mipmap::Linear) .setMagnificationFilter(Sampler::Filter::Linear) + .setBaseLevel(1) + .setMaxLevel(750) .setWrapping(Sampler::Wrapping::ClampToBorder) .setBorderColor(Color3(0.5f)) .setMaxAnisotropy(Sampler::maxMaxAnisotropy()); MAGNUM_VERIFY_NO_ERROR(); } + +void TextureArrayGLTest::samplingBorderInteger1D() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::texture_integer::string() + std::string(" is not supported.")); + + Texture1DArray a; + a.setWrapping(Sampler::Wrapping::ClampToBorder) + .setBorderColor(Vector4i(1, 56, 78, -2)); + Texture1DArray b; + b.setWrapping(Sampler::Wrapping::ClampToBorder) + .setBorderColor(Vector4ui(35, 56, 78, 15)); + + MAGNUM_VERIFY_NO_ERROR(); +} #endif -void TextureGLTest::sampling2DArray() { +void TextureArrayGLTest::sampling2D() { #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -200,6 +296,10 @@ void TextureGLTest::sampling2DArray() { Texture2DArray texture; texture.setMinificationFilter(Sampler::Filter::Linear, Sampler::Mipmap::Linear) .setMagnificationFilter(Sampler::Filter::Linear) + #ifndef MAGNUM_TARGET_GLES2 + .setBaseLevel(1) + .setMaxLevel(750) + #endif #ifndef MAGNUM_TARGET_GLES .setWrapping(Sampler::Wrapping::ClampToBorder) .setBorderColor(Color3(0.5f)) @@ -211,8 +311,34 @@ void TextureGLTest::sampling2DArray() { MAGNUM_VERIFY_NO_ERROR(); } -#ifdef MAGNUM_TARGET_GLES -void TextureGLTest::samplingBorder2DArray() { +#ifdef MAGNUM_TARGET_GLES2 +void TextureArrayGLTest::samplingMaxLevel2D() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::APPLE::texture_max_level::string() + std::string(" is not supported.")); + + Texture2DArray texture; + texture.setMaxLevel(750); + + MAGNUM_VERIFY_NO_ERROR(); +} +#endif + +#ifndef MAGNUM_TARGET_GLES +void TextureArrayGLTest::samplingBorderInteger2D() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::texture_integer::string() + std::string(" is not supported.")); + + Texture2DArray a; + a.setWrapping(Sampler::Wrapping::ClampToBorder) + .setBorderColor(Vector4i(1, 56, 78, -2)); + Texture2DArray b; + b.setWrapping(Sampler::Wrapping::ClampToBorder) + .setBorderColor(Vector4ui(35, 56, 78, 15)); + + MAGNUM_VERIFY_NO_ERROR(); +} +#else +void TextureArrayGLTest::samplingBorder2D() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::NV::texture_border_clamp::string() + std::string(" is not supported.")); @@ -225,7 +351,7 @@ void TextureGLTest::samplingBorder2DArray() { #endif #ifndef MAGNUM_TARGET_GLES -void TextureGLTest::storage1DArray() { +void TextureArrayGLTest::storage1D() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -245,7 +371,7 @@ void TextureGLTest::storage1DArray() { } #endif -void TextureGLTest::storage2DArray() { +void TextureArrayGLTest::storage2D() { #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -270,7 +396,7 @@ void TextureGLTest::storage2DArray() { } #ifndef MAGNUM_TARGET_GLES -void TextureGLTest::image1DArray() { +void TextureArrayGLTest::image1D() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -294,7 +420,7 @@ void TextureGLTest::image1DArray() { std::vector(data, data + 16), TestSuite::Compare::Container); } -void TextureGLTest::image1DArrayBuffer() { +void TextureArrayGLTest::image1DBuffer() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -320,7 +446,7 @@ void TextureGLTest::image1DArrayBuffer() { } #endif -void TextureGLTest::image2DArray() { +void TextureArrayGLTest::image2D() { #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -353,7 +479,7 @@ void TextureGLTest::image2DArray() { #endif } -void TextureGLTest::image2DArrayBuffer() { +void TextureArrayGLTest::image2DBuffer() { #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -388,7 +514,7 @@ void TextureGLTest::image2DArrayBuffer() { } #ifndef MAGNUM_TARGET_GLES -void TextureGLTest::subImage1DArray() { +void TextureArrayGLTest::subImage1D() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -419,7 +545,7 @@ void TextureGLTest::subImage1DArray() { }), TestSuite::Compare::Container); } -void TextureGLTest::subImage1DArrayBuffer() { +void TextureArrayGLTest::subImage1DBuffer() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -452,7 +578,7 @@ void TextureGLTest::subImage1DArrayBuffer() { } #endif -void TextureGLTest::subImage2DArray() { +void TextureArrayGLTest::subImage2D() { #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -507,7 +633,7 @@ void TextureGLTest::subImage2DArray() { #endif } -void TextureGLTest::subImage2DArrayBuffer() { +void TextureArrayGLTest::subImage2DBuffer() { #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -564,7 +690,7 @@ void TextureGLTest::subImage2DArrayBuffer() { } #ifndef MAGNUM_TARGET_GLES -void TextureGLTest::generateMipmap1DArray() { +void TextureArrayGLTest::generateMipmap1D() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::framebuffer_object::string() + std::string(" is not supported.")); if(!Context::current()->isExtensionSupported()) @@ -592,7 +718,7 @@ void TextureGLTest::generateMipmap1DArray() { } #endif -void TextureGLTest::generateMipmap2DArray() { +void TextureArrayGLTest::generateMipmap2D() { #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::ARB::framebuffer_object::string() + std::string(" is not supported.")); @@ -627,7 +753,7 @@ void TextureGLTest::generateMipmap2DArray() { } #ifndef MAGNUM_TARGET_GLES -void TextureGLTest::invalidateImage1DArray() { +void TextureArrayGLTest::invalidateImage1D() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -639,7 +765,7 @@ void TextureGLTest::invalidateImage1DArray() { } #endif -void TextureGLTest::invalidateImage2DArray() { +void TextureArrayGLTest::invalidateImage2D() { #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -653,7 +779,7 @@ void TextureGLTest::invalidateImage2DArray() { } #ifndef MAGNUM_TARGET_GLES -void TextureGLTest::invalidateSubImage1DArray() { +void TextureArrayGLTest::invalidateSubImage1D() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -665,7 +791,7 @@ void TextureGLTest::invalidateSubImage1DArray() { } #endif -void TextureGLTest::invalidateSubImage2DArray() { +void TextureArrayGLTest::invalidateSubImage2D() { #ifndef MAGNUM_TARGET_GLES if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::EXT::texture_array::string() + std::string(" is not supported.")); @@ -680,4 +806,4 @@ void TextureGLTest::invalidateSubImage2DArray() { }} -CORRADE_TEST_MAIN(Magnum::Test::TextureGLTest) +CORRADE_TEST_MAIN(Magnum::Test::TextureArrayGLTest) diff --git a/src/Magnum/Test/TextureGLTest.cpp b/src/Magnum/Test/TextureGLTest.cpp index 79a9368ae..ee23b0316 100644 --- a/src/Magnum/Test/TextureGLTest.cpp +++ b/src/Magnum/Test/TextureGLTest.cpp @@ -48,13 +48,27 @@ class TextureGLTest: public AbstractOpenGLTester { void construct2D(); void construct3D(); + #ifndef MAGNUM_TARGET_GLES + void bind1D(); + #endif + void bind2D(); + void bind3D(); + #ifndef MAGNUM_TARGET_GLES void sampling1D(); #endif void sampling2D(); void sampling3D(); - #ifdef MAGNUM_TARGET_GLES + #ifdef MAGNUM_TARGET_GLES2 + void samplingMaxLevel2D(); + void samplingMaxLevel3D(); + #endif + + #ifndef MAGNUM_TARGET_GLES + void samplingBorderInteger2D(); + void samplingBorderInteger3D(); + #else void samplingBorder2D(); void samplingBorder3D(); #endif @@ -120,13 +134,27 @@ TextureGLTest::TextureGLTest() { &TextureGLTest::construct2D, &TextureGLTest::construct3D, + #ifndef MAGNUM_TARGET_GLES + &TextureGLTest::bind1D, + #endif + &TextureGLTest::bind2D, + &TextureGLTest::bind3D, + #ifndef MAGNUM_TARGET_GLES &TextureGLTest::sampling1D, #endif &TextureGLTest::sampling2D, &TextureGLTest::sampling3D, - #ifdef MAGNUM_TARGET_GLES + #ifdef MAGNUM_TARGET_GLES2 + &TextureGLTest::samplingMaxLevel2D, + &TextureGLTest::samplingMaxLevel3D, + #endif + + #ifndef MAGNUM_TARGET_GLES + &TextureGLTest::samplingBorderInteger2D, + &TextureGLTest::samplingBorderInteger3D, + #else &TextureGLTest::samplingBorder2D, &TextureGLTest::samplingBorder3D, #endif @@ -223,11 +251,90 @@ void TextureGLTest::construct3D() { MAGNUM_VERIFY_NO_ERROR(); } +#ifndef MAGNUM_TARGET_GLES +void TextureGLTest::bind1D() { + Texture1D texture; + + if(Context::current()->isExtensionSupported()) { + CORRADE_EXPECT_FAIL("With ARB_multi_bind the texture must be associated with given target at least once before binding it."); + texture.setBaseLevel(0); + CORRADE_VERIFY(false); + } + + texture.bind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::bind(7, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); +} +#endif + +void TextureGLTest::bind2D() { + Texture2D texture; + + #ifndef MAGNUM_TARGET_GLES + if(Context::current()->isExtensionSupported()) { + CORRADE_EXPECT_FAIL("With ARB_multi_bind the texture must be associated with given target at least once before binding it."); + texture.setBaseLevel(0); + CORRADE_VERIFY(false); + } + #endif + + texture.bind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::bind(7, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); +} + +void TextureGLTest::bind3D() { + #ifdef MAGNUM_TARGET_GLES2 + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::OES::texture_3D::string() + std::string(" is not supported.")); + #endif + + Texture3D texture; + + #ifndef MAGNUM_TARGET_GLES + if(Context::current()->isExtensionSupported()) { + CORRADE_EXPECT_FAIL("With ARB_multi_bind the texture must be associated with given target at least once before binding it."); + texture.setBaseLevel(0); + CORRADE_VERIFY(false); + } + #endif + + texture.bind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::unbind(15); + + MAGNUM_VERIFY_NO_ERROR(); + + AbstractTexture::bind(7, {&texture, nullptr, &texture}); + + MAGNUM_VERIFY_NO_ERROR(); +} + #ifndef MAGNUM_TARGET_GLES void TextureGLTest::sampling1D() { Texture1D texture; texture.setMinificationFilter(Sampler::Filter::Linear, Sampler::Mipmap::Linear) .setMagnificationFilter(Sampler::Filter::Linear) + .setBaseLevel(1) + .setMaxLevel(750) .setWrapping(Sampler::Wrapping::ClampToBorder) .setBorderColor(Color3(0.5f)) .setMaxAnisotropy(Sampler::maxMaxAnisotropy()); @@ -240,6 +347,10 @@ void TextureGLTest::sampling2D() { Texture2D texture; texture.setMinificationFilter(Sampler::Filter::Linear, Sampler::Mipmap::Linear) .setMagnificationFilter(Sampler::Filter::Linear) + #ifndef MAGNUM_TARGET_GLES2 + .setBaseLevel(1) + .setMaxLevel(750) + #endif #ifndef MAGNUM_TARGET_GLES .setWrapping(Sampler::Wrapping::ClampToBorder) .setBorderColor(Color3(0.5f)) @@ -251,7 +362,33 @@ void TextureGLTest::sampling2D() { MAGNUM_VERIFY_NO_ERROR(); } -#ifdef MAGNUM_TARGET_GLES +#ifdef MAGNUM_TARGET_GLES2 +void TextureGLTest::samplingMaxLevel2D() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::APPLE::texture_max_level::string() + std::string(" is not supported.")); + + Texture2D texture; + texture.setMaxLevel(750); + + MAGNUM_VERIFY_NO_ERROR(); +} +#endif + +#ifndef MAGNUM_TARGET_GLES +void TextureGLTest::samplingBorderInteger2D() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::texture_integer::string() + std::string(" is not supported.")); + + Texture2D a; + a.setWrapping(Sampler::Wrapping::ClampToBorder) + .setBorderColor(Vector4i(1, 56, 78, -2)); + Texture2D b; + b.setWrapping(Sampler::Wrapping::ClampToBorder) + .setBorderColor(Vector4ui(35, 56, 78, 15)); + + MAGNUM_VERIFY_NO_ERROR(); +} +#else void TextureGLTest::samplingBorder2D() { if(!Context::current()->isExtensionSupported()) CORRADE_SKIP(Extensions::GL::NV::texture_border_clamp::string() + std::string(" is not supported.")); @@ -273,6 +410,10 @@ void TextureGLTest::sampling3D() { Texture3D texture; texture.setMinificationFilter(Sampler::Filter::Linear, Sampler::Mipmap::Linear) .setMagnificationFilter(Sampler::Filter::Linear) + #ifndef MAGNUM_TARGET_GLES2 + .setBaseLevel(1) + .setMaxLevel(750) + #endif #ifndef MAGNUM_TARGET_GLES .setWrapping(Sampler::Wrapping::ClampToBorder) .setBorderColor(Color3(0.5f)) @@ -284,7 +425,35 @@ void TextureGLTest::sampling3D() { MAGNUM_VERIFY_NO_ERROR(); } -#ifdef MAGNUM_TARGET_GLES +#ifdef MAGNUM_TARGET_GLES2 +void TextureGLTest::samplingMaxLevel3D() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::OES::texture_3D::string() + std::string(" is not supported.")); + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::APPLE::texture_max_level::string() + std::string(" is not supported.")); + + Texture3D texture; + texture.setMaxLevel(750); + + MAGNUM_VERIFY_NO_ERROR(); +} +#endif + +#ifndef MAGNUM_TARGET_GLES +void TextureGLTest::samplingBorderInteger3D() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::texture_integer::string() + std::string(" is not supported.")); + + Texture3D a; + a.setWrapping(Sampler::Wrapping::ClampToBorder) + .setBorderColor(Vector4i(1, 56, 78, -2)); + Texture3D b; + b.setWrapping(Sampler::Wrapping::ClampToBorder) + .setBorderColor(Vector4ui(35, 56, 78, 15)); + + MAGNUM_VERIFY_NO_ERROR(); +} +#else void TextureGLTest::samplingBorder3D() { #ifdef MAGNUM_TARGET_GLES2 if(!Context::current()->isExtensionSupported()) diff --git a/src/Magnum/Text/CMakeLists.txt b/src/Magnum/Text/CMakeLists.txt index 7206c9002..c742bd3f8 100644 --- a/src/Magnum/Text/CMakeLists.txt +++ b/src/Magnum/Text/CMakeLists.txt @@ -46,6 +46,7 @@ if(MAGNUM_BUILD_DEPRECATED) endif() add_library(MagnumText ${SHARED_OR_STATIC} ${MagnumText_SRCS}) +set_target_properties(MagnumText PROPERTIES DEBUG_POSTFIX "-d") if(BUILD_STATIC_PIC) # TODO: CMake 2.8.9 has this as POSITION_INDEPENDENT_CODE property set_target_properties(MagnumText PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") diff --git a/src/Magnum/Texture.h b/src/Magnum/Texture.h index 4a48754c3..0c14f171c 100644 --- a/src/Magnum/Texture.h +++ b/src/Magnum/Texture.h @@ -77,10 +77,12 @@ texture.setMagnificationFilter(Sampler::Filter::Linear) .generateMipmap(); @endcode -@attention Note that default configuration (if @ref setMinificationFilter() is - not called with another value) is to use mipmaps, so be sure to either call - @ref setMinificationFilter(), explicitly specify all mip levels with - @ref setStorage() and @ref setImage() or call @ref generateMipmap(). +@attention Note that default configuration is to use mipmaps. Be sure to either + reduce mip level count using @ref setBaseLevel() and @ref setMaxLevel(), + explicitly allocate all mip levels using @ref setStorage(), call + @ref generateMipmap() after uploading the base level to generate the rest + of the mip chain or call @ref setMinificationFilter() with another value to + disable mipmapping. In shader, the texture is used via `sampler1D`/`sampler2D`/`sampler3D`, `sampler1DShadow`/`sampler2DShadow`/`sampler3DShadow`, @@ -183,10 +185,11 @@ template class Texture: public AbstractTexture { * * The result is not cached in any way. If * @extension{EXT,direct_state_access} is not available, the texture - * is bound to some layer before the operation. + * is bound to some texture unit before the operation. * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and * @fn_gl{GetTexLevelParameter} or @fn_gl_extension{GetTextureLevelParameter,EXT,direct_state_access} - * with @def_gl{TEXTURE_WIDTH}, @def_gl{TEXTURE_HEIGHT} or @def_gl{TEXTURE_DEPTH}. + * with @def_gl{TEXTURE_WIDTH}, @def_gl{TEXTURE_HEIGHT} or + * @def_gl{TEXTURE_DEPTH} * @requires_gl %Texture image queries are not available in OpenGL ES. */ typename DimensionTraits::VectorType imageSize(Int level) { @@ -194,6 +197,47 @@ template class Texture: public AbstractTexture { } #endif + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Set base mip level + * @return Reference to self (for method chaining) + * + * Taken into account when generating mipmap using @ref generateMipmap() + * and when considering texture completeness when using mipmap + * filtering. Initial value is `0`. + * @see @ref setMaxLevel(), @ref setMinificationFilter(), + * @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and @fn_gl{TexParameter} + * or @fn_gl_extension{TextureParameter,EXT,direct_state_access} + * with @def_gl{TEXTURE_BASE_LEVEL} + * @requires_gles30 Base level is always `0` in OpenGL ES 2.0. + */ + Texture& setBaseLevel(Int level) { + AbstractTexture::setBaseLevel(level); + return *this; + } + #endif + + /** + * @brief Set max mip level + * @return Reference to self (for method chaining) + * + * Taken into account when generating mipmap using @ref generateMipmap() + * and when considering texture completeness when using mipmap + * filtering. Initial value is `1000`, which is clamped to count of + * levels specified when using @ref setStorage(). + * @see @ref setBaseLevel(), @ref setMinificationFilter(), + * @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and @fn_gl{TexParameter} + * or @fn_gl_extension{TextureParameter,EXT,direct_state_access} + * with @def_gl{TEXTURE_MAX_LEVEL} + * @requires_gles30 %Extension @es_extension{APPLE,texture_max_level}, + * otherwise the max level is always set to largest possible value + * in OpenGL ES 2.0. + */ + Texture& setMaxLevel(Int level) { + AbstractTexture::setMaxLevel(level); + return *this; + } + /** * @brief Set minification filter * @param filter Filter @@ -204,11 +248,13 @@ template class Texture: public AbstractTexture { * * Sets filter used when the object pixel size is smaller than the * texture size. If @extension{EXT,direct_state_access} is not - * available, the texture is bound to some layer before the operation. - * Initial value is (@ref Sampler::Filter::Nearest, @ref Sampler::Mipmap::Linear). - * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and @fn_gl{TexParameter} - * or @fn_gl_extension{TextureParameter,EXT,direct_state_access} - * with @def_gl{TEXTURE_MIN_FILTER} + * available, the texture is bound to some texture unit before the + * operation. Initial value is {@ref Sampler::Filter::Nearest, + * @ref Sampler::Mipmap::Linear}. + * @see @ref setBaseLevel(), @ref setMaxLevel(), @fn_gl{ActiveTexture}, + * @fn_gl{BindTexture} and @fn_gl{TexParameter} or + * @fn_gl_extension{TextureParameter,EXT,direct_state_access} with + * @def_gl{TEXTURE_MIN_FILTER} */ Texture& setMinificationFilter(Sampler::Filter filter, Sampler::Mipmap mipmap = Sampler::Mipmap::Base) { AbstractTexture::setMinificationFilter(filter, mipmap); @@ -222,8 +268,8 @@ template class Texture: public AbstractTexture { * * Sets filter used when the object pixel size is larger than largest * texture size. If @extension{EXT,direct_state_access} is not - * available, the texture is bound to some layer before the operation. - * Initial value is @ref Sampler::Filter::Linear. + * available, the texture is bound to some texture unit before the + * operation. Initial value is @ref Sampler::Filter::Linear. * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and @fn_gl{TexParameter} * or @fn_gl_extension{TextureParameter,EXT,direct_state_access} * with @def_gl{TEXTURE_MAG_FILTER} @@ -238,11 +284,10 @@ template class Texture: public AbstractTexture { * @param wrapping Wrapping type for all texture dimensions * @return Reference to self (for method chaining) * - * Sets wrapping type for coordinates out of range (0, 1) for normal - * textures and (0, textureSizeInGivenDirection-1) for rectangle - * textures. If @extension{EXT,direct_state_access} is not available, - * the texture is bound to some layer before the operation. Initial - * value is @ref Sampler::Wrapping::Repeat. + * Sets wrapping type for coordinates out of range (0.0f, 1.0f). If + * @extension{EXT,direct_state_access} is not available, the texture is + * bound to some texture unit before the operation. Initial value is + * @ref Sampler::Wrapping::Repeat. * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and @fn_gl{TexParameter} * or @fn_gl_extension{TextureParameter,EXT,direct_state_access} * with @def_gl{TEXTURE_WRAP_S}, @def_gl{TEXTURE_WRAP_T}, @@ -259,7 +304,7 @@ template class Texture: public AbstractTexture { * * Border color when wrapping is set to @ref Sampler::Wrapping::ClampToBorder. * If @extension{EXT,direct_state_access} is not available, the texture - * is bound to some layer before the operation. Initial value is + * is bound to some texture unit before the operation. Initial value is * `{0.0f, 0.0f, 0.0f, 0.0f}`. * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and @fn_gl{TexParameter} * or @fn_gl_extension{TextureParameter,EXT,direct_state_access} @@ -271,6 +316,34 @@ template class Texture: public AbstractTexture { return *this; } + #ifndef MAGNUM_TARGET_GLES + /** + * @brief Set border color for integer texture + * @return Reference to self (for method chaining) + * + * Border color for integer textures when wrapping is set to + * @ref Sampler::Wrapping::ClampToBorder. If @extension{EXT,direct_state_access} + * is not available, the texture is bound to some texture unit before + * the operation. Initial value is `{0, 0, 0, 0}`. + * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and @fn_gl{TexParameter} + * or @fn_gl_extension{TextureParameter,EXT,direct_state_access} + * with @def_gl{TEXTURE_BORDER_COLOR} + * @requires_gl30 %Extension @extension{EXT,texture_integer} + * @requires_gl Border is available only for float textures in OpenGL + * ES. + */ + Texture& setBorderColor(const Vector4ui& color) { + AbstractTexture::setBorderColor(color); + return *this; + } + + /** @overload */ + Texture& setBorderColor(const Vector4i& color) { + AbstractTexture::setBorderColor(color); + return *this; + } + #endif + /** * @brief Set max anisotropy * @return Reference to self (for method chaining) @@ -280,7 +353,7 @@ template class Texture: public AbstractTexture { * @extension{EXT,texture_filter_anisotropic} (desktop or ES) is not * available, this function does nothing. If * @extension{EXT,direct_state_access} is not available, the texture is - * bound to some layer before the operation. + * bound to some texture unit before the operation. * @see @ref Sampler::maxMaxAnisotropy(), @fn_gl{ActiveTexture}, * @fn_gl{BindTexture} and @fn_gl{TexParameter} or * @fn_gl_extension{TextureParameter,EXT,direct_state_access} with @@ -305,19 +378,22 @@ template class Texture: public AbstractTexture { * allowed. * * If @extension{EXT,direct_state_access} is not available, the texture - * is bound to some layer before the operation. If @extension{ARB,texture_storage} - * (part of OpenGL 4.2), OpenGL ES 3.0 or @es_extension{EXT,texture_storage} - * in OpenGL ES 2.0 is not available, the feature is emulated with - * sequence of @ref setImage() calls. - * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and - * @fn_gl{TexStorage1D}/@fn_gl{TexStorage2D}/@fn_gl{TexStorage3D} + * is bound to some texture unit before the operation. If + * @extension{ARB,texture_storage} (part of OpenGL 4.2), OpenGL ES 3.0 + * or @es_extension{EXT,texture_storage} in OpenGL ES 2.0 is not + * available, the feature is emulated with sequence of @ref setImage() + * calls. + * @todo allow the user to specify ColorType explicitly to avoid + * issues in WebGL (see setSubImage()) + * @see @ref setMaxLevel(), @fn_gl{ActiveTexture}, @fn_gl{BindTexture} + * and @fn_gl{TexStorage1D}/@fn_gl{TexStorage2D}/@fn_gl{TexStorage3D} * or @fn_gl_extension{TextureStorage1D,EXT,direct_state_access}/ * @fn_gl_extension{TextureStorage2D,EXT,direct_state_access}/ * @fn_gl_extension{TextureStorage3D,EXT,direct_state_access}, * eventually @fn_gl{TexImage1D}/@fn_gl{TexImage2D}/@fn_gl{TexImage3D} or * @fn_gl_extension{TextureImage1D,EXT,direct_state_access}/ * @fn_gl_extension{TextureImage2D,EXT,direct_state_access}/ - * @fn_gl_extension{TextureImage3D,EXT,direct_state_access}. + * @fn_gl_extension{TextureImage3D,EXT,direct_state_access} */ Texture& setStorage(Int levels, TextureFormat internalFormat, const typename DimensionTraits::VectorType& size) { DataHelper::setStorage(*this, _target, levels, internalFormat, size); @@ -335,7 +411,7 @@ template class Texture: public AbstractTexture { * @ref imageSize(). * * If @extension{EXT,direct_state_access} is not available, the - * texture is bound to some layer before the operation. If + * texture is bound to some texture unit before the operation. If * @extension{ARB,robustness} is available, the operation is protected * from buffer overflow. However, if both @extension{EXT,direct_state_access} * and @extension{ARB,robustness} are available, the DSA version is @@ -379,7 +455,7 @@ template class Texture: public AbstractTexture { * @ref setStorage() and @ref setSubImage() instead. * * If @extension{EXT,direct_state_access} is not available, the - * texture is bound to some layer before the operation. + * texture is bound to some texture unit before the operation. * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and * @fn_gl{TexImage1D}/@fn_gl{TexImage2D}/@fn_gl{TexImage3D} or * @fn_gl_extension{TextureImage1D,EXT,direct_state_access}/ @@ -413,7 +489,13 @@ template class Texture: public AbstractTexture { * @return Reference to self (for method chaining) * * If @extension{EXT,direct_state_access} is not available, the - * texture is bound to some layer before the operation. + * texture is bound to some texture unit before the operation. + * + * @attention In @ref MAGNUM_TARGET_WEBGL "WebGL" the @ref ColorType of + * data passed in @p image must match the original one specified + * in @ref setImage(). It means that you might not be able to use + * @ref setStorage() as it uses implicit @ref ColorType value. + * * @see @ref setStorage(), @ref setImage(), @fn_gl{ActiveTexture}, * @fn_gl{BindTexture} and @fn_gl{TexSubImage1D}/ * @fn_gl{TexSubImage2D}/@fn_gl{TexSubImage3D} or @@ -444,7 +526,7 @@ template class Texture: public AbstractTexture { * @return Reference to self (for method chaining) * * If @extension{EXT,direct_state_access} is not available, the texture - * is bound to some layer before the operation. + * is bound to some texture unit before the operation. * @see setMinificationFilter(), @fn_gl{ActiveTexture}, * @fn_gl{BindTexture} and @fn_gl{GenerateMipmap} or * @fn_gl_extension{GenerateTextureMipmap,EXT,direct_state_access} diff --git a/src/Magnum/TextureArray.h b/src/Magnum/TextureArray.h index 412bf6503..bdf197a88 100644 --- a/src/Magnum/TextureArray.h +++ b/src/Magnum/TextureArray.h @@ -55,6 +55,8 @@ Template class for one- and two-dimensional texture arrays. See also @section Texture-usage Usage +See @ref Texture documentation for introduction. + Common usage is to fully configure all texture parameters and then set the data. Example configuration: @code @@ -77,11 +79,6 @@ for(std::size_t i = 0; i != 16; ++i) { } @endcode -@attention Note that default configuration (if @ref setMinificationFilter() is - not called with another value) is to use mipmaps, so be sure to either call - @ref setMinificationFilter(), explicitly specify all mip levels with - @ref setStorage() and @ref setImage() or call @ref generateMipmap(). - In shader, the texture is used via `sampler1DArray`/`sampler2DArray`, `sampler1DArrayShadow`/`sampler1DArrayShadow`, `isampler1DArray`/`isampler2DArray` or `usampler1DArray`/`usampler2DArray`. See @ref AbstractShaderProgram @@ -103,10 +100,25 @@ template class TextureArray: public AbstractTexture { * @brief Constructor * * Creates new OpenGL texture object. - * @see @fn_gl{GenTextures} with @def_gl{TEXTURE_1D_ARRAY} or @def_gl{TEXTURE_2D_ARRAY} + * @see @fn_gl{GenTextures} with @def_gl{TEXTURE_1D_ARRAY} or + * @def_gl{TEXTURE_2D_ARRAY} */ explicit TextureArray(): AbstractTexture(Implementation::textureArrayTarget()) {} + #ifndef MAGNUM_TARGET_GLES2 + /** @copydoc Texture::setBaseLevel() */ + TextureArray& setBaseLevel(Int level) { + AbstractTexture::setBaseLevel(level); + return *this; + } + #endif + + /** @copydoc Texture::setMaxLevel() */ + TextureArray& setMaxLevel(Int level) { + AbstractTexture::setMaxLevel(level); + return *this; + } + /** @copydoc Texture::setMinificationFilter() */ TextureArray& setMinificationFilter(Sampler::Filter filter, Sampler::Mipmap mipmap = Sampler::Mipmap::Base) { AbstractTexture::setMinificationFilter(filter, mipmap); @@ -131,6 +143,20 @@ template class TextureArray: public AbstractTexture { return *this; } + #ifndef MAGNUM_TARGET_GLES + /** @copydoc Texture::setBorderColor(const Vector4ui&) */ + TextureArray& setBorderColor(const Vector4ui& color) { + AbstractTexture::setBorderColor(color); + return *this; + } + + /** @copydoc Texture::setBorderColor(const Vector4i&) */ + TextureArray& setBorderColor(const Vector4i& color) { + AbstractTexture::setBorderColor(color); + return *this; + } + #endif + /** @copydoc Texture::setMaxAnisotropy() */ TextureArray& setMaxAnisotropy(Float anisotropy) { AbstractTexture::setMaxAnisotropy(anisotropy); @@ -158,7 +184,7 @@ template class TextureArray: public AbstractTexture { * allowed. * * If @extension{EXT,direct_state_access} is not available, the texture - * is bound to some layer before the operation. If + * is bound to some texture unit before the operation. If * @extension{ARB,texture_storage} (part of OpenGL 4.2) or OpenGL ES * 3.0 is not available, the feature is emulated with sequence of * @ref setImage() calls. @@ -200,7 +226,7 @@ template class TextureArray: public AbstractTexture { * @ref setStorage() and @ref setSubImage() instead. * * If @extension{EXT,direct_state_access} is not available, the - * texture is bound to some layer before the operation. + * texture is bound to some texture unit before the operation. * @see @fn_gl{ActiveTexture}, @fn_gl{BindTexture} and * @fn_gl{TexImage2D}/@fn_gl{TexImage3D} or * @fn_gl_extension{TextureImage2D,EXT,direct_state_access}/ @@ -233,7 +259,7 @@ template class TextureArray: public AbstractTexture { * @return Reference to self (for method chaining) * * If @extension{EXT,direct_state_access} is not available, the - * texture is bound to some layer before the operation. + * texture is bound to some texture unit before the operation. * @see @ref setStorage(), @ref setImage(), @fn_gl{ActiveTexture}, * @fn_gl{BindTexture} and @fn_gl{TexSubImage2D}/@fn_gl{TexSubImage3D} * or @fn_gl_extension{TextureSubImage2D,EXT,direct_state_access}/ diff --git a/src/Magnum/TextureFormat.h b/src/Magnum/TextureFormat.h index a1387b240..b173c7960 100644 --- a/src/Magnum/TextureFormat.h +++ b/src/Magnum/TextureFormat.h @@ -674,82 +674,94 @@ enum class TextureFormat: GLenum { #ifndef MAGNUM_TARGET_GLES /** - * Compressed red channel, normalized unsigned. + * Compressed red channel, normalized unsigned. **Not available on + * multisample textures.** * @requires_gl30 %Extension @extension{ARB,texture_rg} * @requires_gl Generic texture compression is not available in OpenGL ES. */ CompressedRed = GL_COMPRESSED_RED, /** - * Compressed red and green channel, normalized unsigned. + * Compressed red and green channel, normalized unsigned. **Not available + * on multisample textures.** * @requires_gl30 %Extension @extension{ARB,texture_rg} * @requires_gl Generic texture compression is not available in OpenGL ES. */ CompressedRG = GL_COMPRESSED_RG, /** - * Compressed RGB, normalized unsigned. + * Compressed RGB, normalized unsigned. **Not available on multisample + * textures.** * @requires_gl Generic texture compression is not available in OpenGL ES. */ CompressedRGB = GL_COMPRESSED_RGB, /** - * Compressed RGBA, normalized unsigned. + * Compressed RGBA, normalized unsigned. **Not available on multisample + * textures.** * @requires_gl Generic texture compression is not available in OpenGL ES. */ CompressedRGBA = GL_COMPRESSED_RGBA, /** - * RTGC compressed red channel, normalized unsigned. + * RTGC compressed red channel, normalized unsigned. **Not available on + * multisample textures.** * @requires_gl30 %Extension @extension{EXT,texture_compression_rgtc} * @requires_gl RGTC texture compression is not available in OpenGL ES. */ CompressedRedRtgc1 = GL_COMPRESSED_RED_RGTC1, /** - * RTGC compressed red and green channel, normalized unsigned. + * RTGC compressed red and green channel, normalized unsigned. **Not + * available on multisample textures.** * @requires_gl30 %Extension @extension{EXT,texture_compression_rgtc} * @requires_gl RGTC texture compression is not available in OpenGL ES. */ CompressedRGRgtc2 = GL_COMPRESSED_RG_RGTC2, /** - * RTGC compressed red channel, normalized signed. + * RTGC compressed red channel, normalized signed. **Not available on + * multisample textures.** * @requires_gl30 %Extension @extension{EXT,texture_compression_rgtc} * @requires_gl RGTC texture compression is not available in OpenGL ES. */ CompressedSignedRedRgtc1 = GL_COMPRESSED_SIGNED_RED_RGTC1, /** - * RTGC compressed red and green channel, normalized signed. + * RTGC compressed red and green channel, normalized signed. **Not + * available on multisample textures.** * @requires_gl30 %Extension @extension{EXT,texture_compression_rgtc} * @requires_gl RGTC texture compression is not available in OpenGL ES. */ CompressedSignedRGRgtc2 = GL_COMPRESSED_SIGNED_RG_RGTC2, /** - * BPTC compressed RGB, unsigned float. + * BPTC compressed RGB, unsigned float. **Not available on multisample + * textures.** * @requires_gl42 %Extension @extension{ARB,texture_compression_bptc} * @requires_gl BPTC texture compression is not available in OpenGL ES. */ CompressedRGBBptcUnsignedFloat = GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, /** - * BPTC compressed RGB, signed float. + * BPTC compressed RGB, signed float. **Not available on multisample + * textures.** * @requires_gl42 %Extension @extension{ARB,texture_compression_bptc} * @requires_gl BPTC texture compression is not available in OpenGL ES. */ CompressedRGBBptcSignedFloat = GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, /** - * BPTC compressed RGBA, normalized unsigned. + * BPTC compressed RGBA, normalized unsigned. **Not available on + * multisample textures.** * @requires_gl42 %Extension @extension{ARB,texture_compression_bptc} * @requires_gl BPTC texture compression is not available in OpenGL ES. */ CompressedRGBABptcUnorm = GL_COMPRESSED_RGBA_BPTC_UNORM, /** - * BPTC compressed sRGBA, normalized unsigned. + * BPTC compressed sRGBA, normalized unsigned. **Not available on + * multisample textures.** * @requires_gl42 %Extension @extension{ARB,texture_compression_bptc} * @requires_gl BPTC texture compression is not available in OpenGL ES. */ diff --git a/src/Magnum/TextureTools/CMakeLists.txt b/src/Magnum/TextureTools/CMakeLists.txt index aa8f6b4e5..19e06bb5c 100644 --- a/src/Magnum/TextureTools/CMakeLists.txt +++ b/src/Magnum/TextureTools/CMakeLists.txt @@ -41,6 +41,7 @@ if(BUILD_STATIC) endif() add_library(MagnumTextureTools ${SHARED_OR_STATIC} ${MagnumTextureTools_SRCS}) +set_target_properties(MagnumTextureTools PROPERTIES DEBUG_POSTFIX "-d") if(BUILD_STATIC_PIC) # TODO: CMake 2.8.9 has this as POSITION_INDEPENDENT_CODE property set_target_properties(MagnumTextureTools PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") diff --git a/src/Magnum/TextureTools/DistanceField.cpp b/src/Magnum/TextureTools/DistanceField.cpp index a68959db2..ccb2aefd6 100644 --- a/src/Magnum/TextureTools/DistanceField.cpp +++ b/src/Magnum/TextureTools/DistanceField.cpp @@ -63,12 +63,12 @@ class DistanceFieldShader: public AbstractShaderProgram { } DistanceFieldShader& setTexture(Texture2D& texture) { - texture.bind(TextureLayer); + texture.bind(TextureUnit); return *this; } private: - enum: Int { TextureLayer = 8 }; + enum: Int { TextureUnit = 8 }; Int radiusUniform, scalingUniform, @@ -130,7 +130,7 @@ DistanceFieldShader::DistanceFieldShader(): radiusUniform(0), scalingUniform(1) if(!Context::current()->isExtensionSupported()) #endif { - setUniform(uniformLocation("textureData"), TextureLayer); + setUniform(uniformLocation("textureData"), TextureUnit); } } diff --git a/src/Magnum/Trade/ImageData.h b/src/Magnum/Trade/ImageData.h index 3ce4980ae..167ee6f38 100644 --- a/src/Magnum/Trade/ImageData.h +++ b/src/Magnum/Trade/ImageData.h @@ -109,8 +109,8 @@ template class ImageData: public AbstractImage { /** * @brief Release data storage * - * Returns the data pointer and resets internal state to default. - * Deleting the returned array is user responsibility. + * Releases the ownership of the data pointer and resets internal state + * to default. Deleting the returned array is then user responsibility. * @see @ref data() */ unsigned char* release(); diff --git a/src/Magnum/Types.h b/src/Magnum/Types.h index 8a2be413e..ef3de0a72 100644 --- a/src/Magnum/Types.h +++ b/src/Magnum/Types.h @@ -42,8 +42,10 @@ typedef std::uint16_t UnsignedShort; typedef std::int16_t Short; typedef std::uint32_t UnsignedInt; typedef std::int32_t Int; +#ifndef MAGNUM_TARGET_WEBGL typedef std::uint64_t UnsignedLong; typedef std::int64_t Long; +#endif /** @todo C++14: use std::float32_t and std::float_64t [N3626](http://open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3626.pdf) */ typedef float Float; diff --git a/src/Magnum/configure.h.cmake b/src/Magnum/configure.h.cmake index ea20a8f7b..75a46c114 100644 --- a/src/Magnum/configure.h.cmake +++ b/src/Magnum/configure.h.cmake @@ -29,3 +29,4 @@ #cmakedefine MAGNUM_TARGET_GLES2 #cmakedefine MAGNUM_TARGET_GLES3 #cmakedefine MAGNUM_TARGET_DESKTOP_GLES +#cmakedefine MAGNUM_TARGET_WEBGL diff --git a/src/MagnumExternal/Optional/optional.hpp b/src/MagnumExternal/Optional/optional.hpp index 9b0f4b833..f06028341 100644 --- a/src/MagnumExternal/Optional/optional.hpp +++ b/src/MagnumExternal/Optional/optional.hpp @@ -192,6 +192,8 @@ template inline constexpr typename std::remove_reference::type&& co __assert_fail(expr, file, line, ""); # elif defined __native_client__ __assert(expr, line, file); // WHY. + # elif defined __ANDROID__ + __assert(file, line, expr); # elif defined __clang__ || defined __GNU_LIBRARY__ __assert(expr, file, line); # elif defined __GNUC__ diff --git a/src/MagnumPlugins/CMakeLists.txt b/src/MagnumPlugins/CMakeLists.txt index 188c83af9..59dab5adb 100644 --- a/src/MagnumPlugins/CMakeLists.txt +++ b/src/MagnumPlugins/CMakeLists.txt @@ -24,11 +24,11 @@ # # Wrapper for creating given plugin type -macro(add_plugin) +macro(add_plugin plugin_name debug_install_dir release_install_dir metadata_file) if(NOT BUILD_STATIC) - corrade_add_plugin(${ARGN}) + corrade_add_plugin(${plugin_name} ${debug_install_dir} ${release_install_dir} ${metadata_file} ${ARGN}) else() - corrade_add_static_plugin(${ARGN}) + corrade_add_static_plugin(${plugin_name} ${release_install_dir} ${metadata_file} ${ARGN}) endif() endmacro() @@ -40,6 +40,10 @@ if(WITH_TEXT AND WITH_MAGNUMFONTCONVERTER AND NOT MAGNUM_TARGET_GLES) add_subdirectory(MagnumFontConverter) endif() +if(WITH_OBJIMPORTER) + add_subdirectory(ObjImporter) +endif() + if(WITH_TGAIMAGECONVERTER) add_subdirectory(TgaImageConverter) endif() diff --git a/src/MagnumPlugins/MagnumFont/CMakeLists.txt b/src/MagnumPlugins/MagnumFont/CMakeLists.txt index bc68bb215..54026939f 100644 --- a/src/MagnumPlugins/MagnumFont/CMakeLists.txt +++ b/src/MagnumPlugins/MagnumFont/CMakeLists.txt @@ -32,7 +32,7 @@ set(MagnumFont_HEADERS add_library(MagnumFontObjects OBJECT ${MagnumFont_SOURCES}) set_target_properties(MagnumFontObjects PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") -add_plugin(MagnumFont ${MAGNUM_PLUGINS_FONT_INSTALL_DIR} +add_plugin(MagnumFont ${MAGNUM_PLUGINS_FONT_DEBUG_INSTALL_DIR} ${MAGNUM_PLUGINS_FONT_RELEASE_INSTALL_DIR} MagnumFont.conf $ pluginRegistration.cpp) diff --git a/src/MagnumPlugins/MagnumFontConverter/CMakeLists.txt b/src/MagnumPlugins/MagnumFontConverter/CMakeLists.txt index d50d535a1..e171f6c81 100644 --- a/src/MagnumPlugins/MagnumFontConverter/CMakeLists.txt +++ b/src/MagnumPlugins/MagnumFontConverter/CMakeLists.txt @@ -32,7 +32,7 @@ set(MagnumFontConverter_HEADERS add_library(MagnumFontConverterObjects OBJECT ${MagnumFontConverter_SOURCES}) set_target_properties(MagnumFontConverterObjects PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") -add_plugin(MagnumFontConverter ${MAGNUM_PLUGINS_FONTCONVERTER_INSTALL_DIR} +add_plugin(MagnumFontConverter ${MAGNUM_PLUGINS_FONTCONVERTER_DEBUG_INSTALL_DIR} ${MAGNUM_PLUGINS_FONTCONVERTER_RELEASE_INSTALL_DIR} MagnumFontConverter.conf $ pluginRegistration.cpp) diff --git a/src/MagnumPlugins/ObjImporter/CMakeLists.txt b/src/MagnumPlugins/ObjImporter/CMakeLists.txt new file mode 100644 index 000000000..63a09e70b --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/CMakeLists.txt @@ -0,0 +1,53 @@ +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014 +# 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. +# + +add_library(ObjImporterObjects OBJECT ObjImporter.cpp) +if(NOT BUILD_STATIC OR BUILD_STATIC_PIC) + set_target_properties(ObjImporterObjects PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") +endif() + +add_plugin(ObjImporter ${MAGNUM_PLUGINS_IMPORTER_DEBUG_INSTALL_DIR} ${MAGNUM_PLUGINS_IMPORTER_RELEASE_INSTALL_DIR} + ObjImporter.conf + $ + pluginRegistration.cpp) +target_link_libraries(ObjImporter Magnum MagnumMeshTools) + +install(FILES ObjImporter.h DESTINATION ${MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR}/ObjImporter) + +if(BUILD_TESTS) + add_library(ObjImporterTestLib STATIC $) + target_link_libraries(ObjImporterTestLib Magnum MagnumMeshTools) + + # On Windows we need to install first and then run the tests to avoid "DLL + # not found" hell, thus we need to install this too + if(WIN32 AND NOT CMAKE_CROSSCOMPILING) + install(TARGETS ObjImporterTestLib + RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} + LIBRARY DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR} + ARCHIVE DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) + endif() + + add_subdirectory(Test) +endif() diff --git a/src/MagnumPlugins/ObjImporter/ObjImporter.conf b/src/MagnumPlugins/ObjImporter/ObjImporter.conf new file mode 100644 index 000000000..e69de29bb diff --git a/src/MagnumPlugins/ObjImporter/ObjImporter.cpp b/src/MagnumPlugins/ObjImporter/ObjImporter.cpp new file mode 100644 index 000000000..932e509c1 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/ObjImporter.cpp @@ -0,0 +1,449 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + 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 "ObjImporter.h" + +#include +#include +#include +#include +#include +#include + +#include "Magnum/Math/Vector3.h" +#include "Magnum/Trade/MeshData3D.h" +#include +#include +#include + +namespace Magnum { namespace Trade { + +struct ObjImporter::File { + std::unordered_map meshesForName; + std::vector meshNames; + std::vector> meshes; + std::unique_ptr in; +}; + +namespace { + +void ignoreLine(std::istream& in) { + in.ignore(std::numeric_limits::max(), '\n'); +} + +template Math::Vector extractFloatData(std::string str, Float* extra = nullptr) { + std::vector data = Utility::String::splitWithoutEmptyParts(str, ' '); + if(data.size() < size || data.size() > size + (extra ? 1 : 0)) { + Error() << "Trade::ObjImporter::mesh3D(): invalid float array size"; + throw 0; + } + + Math::Vector output; + for(std::size_t i = 0; i != size; ++i) + output[i] = std::stof(data[i]); + + if(data.size() == size+1) *extra = std::stof(data.back()); + + return output; +} + +template void reindex(const std::vector& indices, std::vector& data) { + /* Check that indices are in range */ + for(UnsignedInt i: indices) if(i >= data.size()) { + Error() << "Trade::ObjImporter::mesh3D(): index out of range"; + throw 0; + } + + data = MeshTools::duplicate(indices, data); +} + +} + +ObjImporter::ObjImporter() = default; + +ObjImporter::ObjImporter(PluginManager::AbstractManager& manager, std::string plugin): AbstractImporter(manager, std::move(plugin)) {} + +ObjImporter::~ObjImporter() = default; + +auto ObjImporter::doFeatures() const -> Features { return Feature::OpenData; } + +void ObjImporter::doClose() { _file.reset(); } + +bool ObjImporter::doIsOpened() const { return !!_file; } + +void ObjImporter::doOpenFile(const std::string& filename) { + /* Open file in *text* mode (to avoid \r handling) */ + std::unique_ptr in{new std::ifstream{filename}}; + if(!in->good()) { + Error() << "Trade::ObjImporter::openFile(): cannot open file" << filename; + return; + } + + _file.reset(new File); + _file->in = std::move(in); + parseMeshNames(); +} + +void ObjImporter::doOpenData(Containers::ArrayReference data) { + /* Open file in *text* mode (to avoid \r handling) */ + _file.reset(new File); + _file->in.reset(new std::istringstream{{reinterpret_cast(data.begin()), data.size()}}); + + parseMeshNames(); +} + +void ObjImporter::parseMeshNames() { + /* First mesh starts at the beginning, its indices start from 1. The end + offset will be updated to proper value later. */ + UnsignedInt positionIndexOffset = 1; + UnsignedInt normalIndexOffset = 1; + UnsignedInt textureCoordinateIndexOffset = 1; + _file->meshes.emplace_back(0, 0, positionIndexOffset, normalIndexOffset, textureCoordinateIndexOffset); + + /* The first mesh doesn't have name by default but we might find it later, + so we need to track whether there are any data before first name */ + bool thisIsFirstMeshAndItHasNoData = true; + _file->meshNames.emplace_back(); + + while(_file->in->good()) { + /* The previous object might end at the beginning of this line */ + const std::streampos end = _file->in->tellg(); + + /* Comment line */ + if(_file->in->peek() == '#') { + ignoreLine(*_file->in); + continue; + } + + /* Parse the keyword */ + std::string keyword; + *_file->in >> keyword; + + /* Mesh name */ + if(keyword == "o") { + std::string name; + std::getline(*_file->in, name); + name = Utility::String::trim(name); + + /* This is the name of first mesh */ + if(thisIsFirstMeshAndItHasNoData) { + thisIsFirstMeshAndItHasNoData = false; + + /* Update its name and add it to name map */ + if(!name.empty()) + #ifndef CORRADE_GCC46_COMPATIBILITY + _file->meshesForName.emplace(name, _file->meshes.size() - 1); + #else + _file->meshesForName.insert({name, _file->meshes.size() - 1}); + #endif + _file->meshNames.back() = std::move(name); + + /* Update its begin offset to be more precise */ + std::get<0>(_file->meshes.back()) = _file->in->tellg(); + + /* Otherwise this is a name of new mesh */ + } else { + /* Set end of the previous one */ + std::get<1>(_file->meshes.back()) = end; + + /* Save name and offset of the new one. The end offset will be + updated later. */ + if(!name.empty()) + #ifndef CORRADE_GCC46_COMPATIBILITY + _file->meshesForName.emplace(name, _file->meshes.size()); + #else + _file->meshesForName.insert({name, _file->meshes.size()}); + #endif + _file->meshNames.emplace_back(std::move(name)); + _file->meshes.emplace_back(_file->in->tellg(), 0, positionIndexOffset, textureCoordinateIndexOffset, normalIndexOffset); + } + + continue; + + /* If there are any data/indices before the first name, it means that + the first object is unnamed. We need to check for them. */ + + /* Vertex data, update index offset for the following meshes */ + } else if(keyword == "v") { + ++positionIndexOffset; + thisIsFirstMeshAndItHasNoData = false; + } else if(keyword == "vt") { + ++textureCoordinateIndexOffset; + thisIsFirstMeshAndItHasNoData = false; + } else if(keyword == "vn") { + ++normalIndexOffset; + thisIsFirstMeshAndItHasNoData = false; + + /* Index data, just mark that we found something for first unnamed + object */ + } else if(thisIsFirstMeshAndItHasNoData) for(const std::string& data: {"p", "l", "f"}) { + if(keyword == data) { + thisIsFirstMeshAndItHasNoData = false; + break; + } + } + + /* Ignore the rest of the line */ + ignoreLine(*_file->in); + } + + /* Set end of the last object */ + _file->in->clear(); + _file->in->seekg(0, std::ios::end); + std::get<1>(_file->meshes.back()) = _file->in->tellg(); +} + +UnsignedInt ObjImporter::doMesh3DCount() const { return _file->meshes.size(); } + +Int ObjImporter::doMesh3DForName(const std::string& name) { + const auto it = _file->meshesForName.find(name); + return it == _file->meshesForName.end() ? -1 : it->second; +} + +std::string ObjImporter::doMesh3DName(UnsignedInt id) { + return _file->meshNames[id]; +} + +std::optional ObjImporter::doMesh3D(UnsignedInt id) { + /* Seek the file, set mesh parsing parameters */ + std::streampos begin, end; + UnsignedInt positionIndexOffset, textureCoordinateIndexOffset, normalIndexOffset; + std::tie(begin, end, positionIndexOffset, textureCoordinateIndexOffset, normalIndexOffset) = _file->meshes[id]; + _file->in->seekg(begin); + + std::optional primitive; + std::vector positions; + std::vector> textureCoordinates; + std::vector> normals; + std::vector positionIndices; + std::vector textureCoordinateIndices; + std::vector normalIndices; + + try { while(_file->in->good() && _file->in->tellg() < end) { + /* Ignore comments */ + if(_file->in->peek() == '#') { + ignoreLine(*_file->in); + continue; + } + + /* Get the line */ + std::string line; + std::getline(*_file->in, line); + line = Utility::String::trim(line); + + /* Ignore empty lines */ + if(line.empty()) continue; + + /* Split the line into keyword and contents */ + const std::size_t keywordEnd = line.find(' '); + const std::string keyword = line.substr(0, keywordEnd); + const std::string contents = keywordEnd != std::string::npos ? + Utility::String::ltrim(line.substr(keywordEnd+1)) : ""; + + /* Vertex position */ + if(keyword == "v") { + Float extra{1.0f}; + const Vector3 data = extractFloatData<3>(contents, &extra); + if(!Math::TypeTraits::equals(extra, 1.0f)) { + Error() << "Trade::ObjImporter::mesh3D(): homogeneous coordinates are not supported"; + return std::nullopt; + } + + positions.push_back(data); + + /* Texture coordinate */ + } else if(keyword == "vt") { + Float extra{0.0f}; + const auto data = extractFloatData<2>(contents, &extra); + if(!Math::TypeTraits::equals(extra, 0.0f)) { + Error() << "Trade::ObjImporter::mesh3D(): 3D texture coordinates are not supported"; + return std::nullopt; + } + + if(textureCoordinates.empty()) textureCoordinates.push_back({}); + textureCoordinates.front().push_back(data); + + /* Normal */ + } else if(keyword == "vn") { + if(normals.empty()) normals.push_back({}); + normals.front().push_back(extractFloatData<3>(contents)); + + /* Indices */ + } else if(keyword == "p" || keyword == "l" || keyword == "f") { + const std::vector indexTuples = Utility::String::splitWithoutEmptyParts(contents, ' '); + + /* Points */ + if(keyword == "p") { + /* Check that we don't mix the primitives in one mesh */ + if(primitive && primitive != MeshPrimitive::Points) { + Error() << "Trade::ObjImporter::mesh3D(): mixed primitive" << *primitive << "and" << MeshPrimitive::Points; + return std::nullopt; + } + + /* Check vertex count per primitive */ + if(indexTuples.size() != 1) { + Error() << "Trade::ObjImporter::mesh3D(): wrong index count for point"; + return std::nullopt; + } + + primitive = MeshPrimitive::Points; + + /* Lines */ + } else if(keyword == "l") { + /* Check that we don't mix the primitives in one mesh */ + if(primitive && primitive != MeshPrimitive::Lines) { + Error() << "Trade::ObjImporter::mesh3D(): mixed primitive" << *primitive << "and" << MeshPrimitive::Lines; + return std::nullopt; + } + + /* Check vertex count per primitive */ + if(indexTuples.size() != 2) { + Error() << "Trade::ObjImporter::mesh3D(): wrong index count for line"; + return std::nullopt; + } + + primitive = MeshPrimitive::Lines; + + /* Faces */ + } else if(keyword == "f") { + /* Check that we don't mix the primitives in one mesh */ + if(primitive && primitive != MeshPrimitive::Triangles) { + Error() << "Trade::ObjImporter::mesh3D(): mixed primitive" << *primitive << "and" << MeshPrimitive::Triangles; + return std::nullopt; + } + + /* Check vertex count per primitive */ + if(indexTuples.size() < 3) { + Error() << "Trade::ObjImporter::mesh3D(): wrong index count for triangle"; + return std::nullopt; + } else if(indexTuples.size() != 3) { + Error() << "Trade::ObjImporter::mesh3D(): polygons are not supported"; + return std::nullopt; + } + + primitive = MeshPrimitive::Triangles; + + } else CORRADE_ASSERT_UNREACHABLE(); + + for(const std::string& indexTuple: indexTuples) { + std::vector indices = Utility::String::split(indexTuple, '/'); + if(indices.size() > 3) { + Error() << "Trade::ObjImporter::mesh3D(): invalid index data"; + return std::nullopt; + } + + /* Position indices */ + positionIndices.push_back(std::stoul(indices[0]) - positionIndexOffset); + + /* Texture coordinates */ + if(indices.size() == 2 || (indices.size() == 3 && !indices[1].empty())) + textureCoordinateIndices.push_back(std::stoul(indices[1]) - textureCoordinateIndexOffset); + + /* Normal indices */ + if(indices.size() == 3) + normalIndices.push_back(std::stoul(indices[2]) - normalIndexOffset); + } + + /* Ignore unsupported keywords, error out on unknown keywords */ + } else if(![&keyword](){ + /* Using lambda to emulate for-else construct like in Python */ + for(const std::string expected: {"mtllib", "usemtl", "g", "s"}) + if(keyword == expected) return true; + return false; + }()) { + Error() << "Trade::ObjImporter::mesh3D(): unknown keyword" << keyword; + return std::nullopt; + } + + }} catch(std::exception) { + Error() << "Trade::ObjImporter::mesh3D(): error while converting numeric data"; + return std::nullopt; + } catch(...) { + /* Error message already printed */ + return std::nullopt; + } + + /* There should be at least indexed position data */ + if(positions.empty() || positionIndices.empty()) { + Error() << "Trade::ObjImporter::mesh3D(): incomplete position data"; + return std::nullopt; + } + + /* If there are index data, there should be also vertex data (and also the other way) */ + if(normals.empty() != normalIndices.empty()) { + Error() << "Trade::ObjImporter::mesh3D(): incomplete normal data"; + return std::nullopt; + } + if(textureCoordinates.empty() != textureCoordinateIndices.empty()) { + Error() << "Trade::ObjImporter::mesh3D(): incomplete texture coordinate data"; + return std::nullopt; + } + + /* All index arrays should have the same length */ + if(!normalIndices.empty() && normalIndices.size() != positionIndices.size()) { + CORRADE_INTERNAL_ASSERT(normalIndices.size() < positionIndices.size()); + Error() << "Trade::ObjImporter::mesh3D(): some normal indices are missing"; + return std::nullopt; + } + if(!textureCoordinates.empty() && textureCoordinateIndices.size() != positionIndices.size()) { + CORRADE_INTERNAL_ASSERT(textureCoordinateIndices.size() < positionIndices.size()); + Error() << "Trade::ObjImporter::mesh3D(): some texture coordinate indices are missing"; + return std::nullopt; + } + + /* Merge index arrays, if there aren't just the positions */ + std::vector indices; + if(!normalIndices.empty() || !textureCoordinateIndices.empty()) { + std::vector>> arrays; + arrays.reserve(3); + arrays.push_back(positionIndices); + if(!normalIndices.empty()) arrays.push_back(normalIndices); + if(!textureCoordinateIndices.empty()) arrays.push_back(textureCoordinateIndices); + indices = MeshTools::combineIndexArrays(arrays); + + /* Reindex data arrays */ + try { + reindex(positionIndices, positions); + if(!normalIndices.empty()) reindex(normalIndices, normals.front()); + if(!textureCoordinateIndices.empty()) reindex(textureCoordinateIndices, textureCoordinates.front()); + } catch(...) { + /* Error message already printed */ + return std::nullopt; + } + + /* Otherwise just use the original position index array. Don't forget to + check range */ + } else { + indices = std::move(positionIndices); + for(UnsignedInt i: indices) if(i >= positions.size()) { + Error() << "Trade::ObjImporter::mesh3D(): index out of range"; + return std::nullopt; + } + } + + return MeshData3D(*primitive, std::move(indices), {std::move(positions)}, std::move(normals), std::move(textureCoordinates)); +} + +}} diff --git a/src/MagnumPlugins/ObjImporter/ObjImporter.h b/src/MagnumPlugins/ObjImporter/ObjImporter.h new file mode 100644 index 000000000..cf6931667 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/ObjImporter.h @@ -0,0 +1,86 @@ +#ifndef Magnum_Trade_ObjImporter_h +#define Magnum_Trade_ObjImporter_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + 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::ObjImporter + */ + +#include "Magnum/Trade/AbstractImporter.h" + +namespace Magnum { namespace Trade { + +/** +@brief OBJ importer plugin + +Supported features: +- multiple objects +- vertex positions, normals and 2D texture coordinates +- triangles, lines and points + +Polygons (quads etc.), automatic normal generation and material properties are +currently not supported. + +This plugin is built if `WITH_OBJIMPORTER` is enabled when building %Magnum. To +use dynamic plugin, you need to load `%ObjImporter` plugin from +`MAGNUM_PLUGINS_IMPORTER_DIR`. To use static plugin or use this as a dependency +of another plugin, you need to request `%ObjImporter` component of `%Magnum` +package in CMake and link to `${MAGNUM_OBJIMPORTER_LIBRARIES}`. See +@ref building, @ref cmake and @ref plugins for more information. +*/ +class ObjImporter: public AbstractImporter { + public: + /** @brief Default constructor */ + explicit ObjImporter(); + + /** @brief Plugin manager constructor */ + explicit ObjImporter(PluginManager::AbstractManager& manager, std::string plugin); + + ~ObjImporter(); + + private: + struct File; + + Features doFeatures() const override; + + bool doIsOpened() const override; + void doOpenData(Containers::ArrayReference data) override; + void doOpenFile(const std::string& filename) override; + void doClose() override; + + UnsignedInt doMesh3DCount() const override; + Int doMesh3DForName(const std::string& name) override; + std::string doMesh3DName(UnsignedInt id) override; + std::optional doMesh3D(UnsignedInt id) override; + + void parseMeshNames(); + + std::unique_ptr _file; +}; + +}} + +#endif diff --git a/src/MagnumPlugins/ObjImporter/Test/CMakeLists.txt b/src/MagnumPlugins/ObjImporter/Test/CMakeLists.txt new file mode 100644 index 000000000..c5dec7146 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/CMakeLists.txt @@ -0,0 +1,31 @@ +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014 +# 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. +# + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/configure.h) + +include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}) + +corrade_add_test(ObjImporterTest Test.cpp LIBRARIES ObjImporterTestLib) diff --git a/src/MagnumPlugins/ObjImporter/Test/Test.cpp b/src/MagnumPlugins/ObjImporter/Test/Test.cpp new file mode 100644 index 000000000..9b4d858e9 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/Test.cpp @@ -0,0 +1,737 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + 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 "Magnum/Mesh.h" +#include "Magnum/Math/Vector3.h" +#include "Magnum/Trade/MeshData3D.h" +#include "MagnumPlugins/ObjImporter/ObjImporter.h" + +#include "configure.h" + +namespace Magnum { namespace Trade { namespace Test { + +class ObjImporterTest: public TestSuite::Tester { + public: + explicit ObjImporterTest(); + + void pointMesh(); + void lineMesh(); + void triangleMesh(); + void mixedPrimitives(); + + void positionsOnly(); + void textureCoordinates(); + void normals(); + void textureCoordinatesNormals(); + + void emptyFile(); + void unnamedMesh(); + void namedMesh(); + void moreMeshes(); + void unnamedFirstMesh(); + + void wrongFloat(); + void wrongInteger(); + void unmergedIndexOutOfRange(); + void mergedIndexOutOfRange(); + void zeroIndex(); + + void explicitOptionalPositionCoordinate(); + void explicitOptionalTextureCoordinate(); + void unsupportedOptionalPositionCoordinate(); + void unsupportedOptionalTextureCoordinate(); + + void shortFloatData(); + void longFloatData(); + void longOptionalFloatData(); + + void longIndexData(); + void wrongPointIndexData(); + void wrongLineIndexData(); + void wrongTriangleIndexData(); + void polygonIndexData(); + + void missingPositionData(); + void missingNormalData(); + void missingTextureCoordinateData(); + void missingPositionIndices(); + void missingNormalIndices(); + void missingTextureCoordinateIndices(); + + void wrongTextureCoordinateIndexCount(); + void wrongNormalIndexCount(); + + void unsupportedKeyword(); + void unknownKeyword(); +}; + +ObjImporterTest::ObjImporterTest() { + addTests({&ObjImporterTest::pointMesh, + &ObjImporterTest::lineMesh, + &ObjImporterTest::triangleMesh, + &ObjImporterTest::mixedPrimitives, + + &ObjImporterTest::positionsOnly, + &ObjImporterTest::textureCoordinates, + &ObjImporterTest::normals, + &ObjImporterTest::textureCoordinatesNormals, + + &ObjImporterTest::emptyFile, + &ObjImporterTest::unnamedMesh, + &ObjImporterTest::namedMesh, + &ObjImporterTest::moreMeshes, + &ObjImporterTest::unnamedFirstMesh, + + &ObjImporterTest::wrongFloat, + &ObjImporterTest::wrongInteger, + &ObjImporterTest::unmergedIndexOutOfRange, + &ObjImporterTest::mergedIndexOutOfRange, + &ObjImporterTest::zeroIndex, + + &ObjImporterTest::explicitOptionalPositionCoordinate, + &ObjImporterTest::explicitOptionalTextureCoordinate, + &ObjImporterTest::unsupportedOptionalPositionCoordinate, + &ObjImporterTest::unsupportedOptionalTextureCoordinate, + + &ObjImporterTest::shortFloatData, + &ObjImporterTest::longFloatData, + &ObjImporterTest::longOptionalFloatData, + + &ObjImporterTest::longIndexData, + &ObjImporterTest::wrongPointIndexData, + &ObjImporterTest::wrongLineIndexData, + &ObjImporterTest::wrongTriangleIndexData, + + &ObjImporterTest::missingPositionData, + &ObjImporterTest::missingNormalData, + &ObjImporterTest::missingTextureCoordinateData, + &ObjImporterTest::missingPositionIndices, + &ObjImporterTest::missingNormalIndices, + &ObjImporterTest::missingTextureCoordinateIndices, + + &ObjImporterTest::wrongTextureCoordinateIndexCount, + &ObjImporterTest::wrongNormalIndexCount, + + &ObjImporterTest::unsupportedKeyword, + &ObjImporterTest::unknownKeyword}); +} + +void ObjImporterTest::pointMesh() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "pointMesh.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Points); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {2.0f, 3.0f, 5.0f} + })); + CORRADE_COMPARE(data->indices(), (std::vector{ + 0, 2, 1, 0 + })); +} + +void ObjImporterTest::lineMesh() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "lineMesh.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Lines); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {2.0f, 3.0f, 5.0f} + })); + CORRADE_COMPARE(data->indices(), (std::vector{ + 0, 1, 1, 2 + })); +} + +void ObjImporterTest::triangleMesh() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "triangleMesh.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Triangles); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {2.0f, 3.0f, 5.0f}, + {2.5f, 0.0f, 1.0f} + })); + CORRADE_COMPARE(data->indices(), (std::vector{ + 0, 1, 2, 3, 1, 0 + })); +} + +void ObjImporterTest::mixedPrimitives() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "mixedPrimitives.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(0)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): mixed primitive MeshPrimitive::Points and MeshPrimitive::Lines\n"); +} + +void ObjImporterTest::positionsOnly() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "triangleMesh.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_VERIFY(!data->hasNormals()); + CORRADE_VERIFY(!data->hasTextureCoords2D()); +} + +void ObjImporterTest::textureCoordinates() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "textureCoordinates.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Lines); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_VERIFY(!data->hasNormals()); + CORRADE_COMPARE(data->textureCoords2DArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f} + })); + CORRADE_COMPARE(data->textureCoords2D(0), (std::vector{ + {1.0f, 0.5f}, + {1.0f, 0.5f}, + {0.5f, 1.0f}, + {0.5f, 1.0f} + })); + CORRADE_COMPARE(data->indices(), (std::vector{ + 0, 1, 2, 3, 1, 0 + })); +} + +void ObjImporterTest::normals() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "normals.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Lines); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_VERIFY(!data->hasTextureCoords2D()); + CORRADE_COMPARE(data->normalArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f} + })); + CORRADE_COMPARE(data->normals(0), (std::vector{ + {1.0f, 0.5f, 3.5f}, + {1.0f, 0.5f, 3.5f}, + {0.5f, 1.0f, 0.5f}, + {0.5f, 1.0f, 0.5f} + })); + CORRADE_COMPARE(data->indices(), (std::vector{ + 0, 1, 2, 3, 1, 0 + })); +} + +void ObjImporterTest::textureCoordinatesNormals() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "textureCoordinatesNormals.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Lines); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_COMPARE(data->textureCoords2DArrayCount(), 1); + CORRADE_COMPARE(data->normalArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {0.0f, 1.5f, 1.0f} + })); + CORRADE_COMPARE(data->textureCoords2D(0), (std::vector{ + {1.0f, 0.5f}, + {1.0f, 0.5f}, + {0.5f, 1.0f}, + {0.5f, 1.0f}, + {0.5f, 1.0f} + })); + CORRADE_COMPARE(data->normals(0), (std::vector{ + {1.0f, 0.5f, 3.5f}, + {0.5f, 1.0f, 0.5f}, + {0.5f, 1.0f, 0.5f}, + {1.0f, 0.5f, 3.5f}, + {0.5f, 1.0f, 0.5f} + })); + CORRADE_COMPARE(data->indices(), (std::vector{ + 0, 1, 2, 3, 1, 0, 4, 2 + })); +} + +void ObjImporterTest::emptyFile() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "emptyFile.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); +} + +void ObjImporterTest::unnamedMesh() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "emptyFile.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + CORRADE_COMPARE(importer.mesh3DName(0), ""); + CORRADE_COMPARE(importer.mesh3DForName(""), -1); +} + +void ObjImporterTest::namedMesh() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "namedMesh.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 1); + CORRADE_COMPARE(importer.mesh3DName(0), "MyMesh"); + CORRADE_COMPARE(importer.mesh3DForName("MyMesh"), 0); +} + +void ObjImporterTest::moreMeshes() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "moreMeshes.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 3); + + CORRADE_COMPARE(importer.mesh3DName(0), "PointMesh"); + CORRADE_COMPARE(importer.mesh3DForName("PointMesh"), 0); + const std::optional data = importer.mesh3D(0); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Points); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f} + })); + CORRADE_COMPARE(data->indices(), (std::vector{ + 0, 1 + })); + + CORRADE_COMPARE(importer.mesh3DName(1), "LineMesh"); + CORRADE_COMPARE(importer.mesh3DForName("LineMesh"), 1); + const std::optional data1 = importer.mesh3D(1); + CORRADE_VERIFY(data1); + CORRADE_COMPARE(data1->primitive(), MeshPrimitive::Lines); + CORRADE_COMPARE(data1->positionArrayCount(), 1); + CORRADE_COMPARE(data1->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f} + })); + CORRADE_COMPARE(data1->indices(), (std::vector{ + 0, 1, 1, 0 + })); + + CORRADE_COMPARE(importer.mesh3DName(2), "TriangleMesh"); + CORRADE_COMPARE(importer.mesh3DForName("TriangleMesh"), 2); + const std::optional data2 = importer.mesh3D(2); + CORRADE_VERIFY(data2); + CORRADE_COMPARE(data2->primitive(), MeshPrimitive::Triangles); + CORRADE_COMPARE(data2->positionArrayCount(), 1); + CORRADE_COMPARE(data2->positions(0), (std::vector{ + {0.5f, 2.0f, 3.0f}, + {0.0f, 1.5f, 1.0f}, + {2.0f, 3.0f, 5.5f} + })); + CORRADE_COMPARE(data2->indices(), (std::vector{ + 0, 1, 2, 2, 1, 0 + })); +} + +void ObjImporterTest::unnamedFirstMesh() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "unnamedFirstMesh.obj"))); + CORRADE_COMPARE(importer.mesh3DCount(), 2); + + CORRADE_COMPARE(importer.mesh3DName(0), ""); + CORRADE_COMPARE(importer.mesh3DForName(""), -1); + + CORRADE_COMPARE(importer.mesh3DName(1), "SecondMesh"); + CORRADE_COMPARE(importer.mesh3DForName("SecondMesh"), 1); +} + +void ObjImporterTest::wrongFloat() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumbers.obj"))); + const Int id = importer.mesh3DForName("WrongFloat"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): error while converting numeric data\n"); +} + +void ObjImporterTest::wrongInteger() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumbers.obj"))); + const Int id = importer.mesh3DForName("WrongInteger"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): error while converting numeric data\n"); +} + +void ObjImporterTest::unmergedIndexOutOfRange() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumbers.obj"))); + const Int id = importer.mesh3DForName("PositionIndexOutOfRange"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): index out of range\n"); +} + +void ObjImporterTest::mergedIndexOutOfRange() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumbers.obj"))); + const Int id = importer.mesh3DForName("TextureIndexOutOfRange"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): index out of range\n"); +} + +void ObjImporterTest::zeroIndex() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumbers.obj"))); + const Int id = importer.mesh3DForName("ZeroIndex"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): index out of range\n"); +} + +void ObjImporterTest::explicitOptionalPositionCoordinate() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "optionalCoordinates.obj"))); + const Int id = importer.mesh3DForName("SupportedPositionW"); + CORRADE_VERIFY(id > -1); + + const std::optional data = importer.mesh3D(id); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {1.5f, 2.0f, 3.0f} + })); +} + +void ObjImporterTest::explicitOptionalTextureCoordinate() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "optionalCoordinates.obj"))); + const Int id = importer.mesh3DForName("SupportedTextureW"); + CORRADE_VERIFY(id > -1); + + const std::optional data = importer.mesh3D(id); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->textureCoords2DArrayCount(), 1); + CORRADE_COMPARE(data->textureCoords2D(0), (std::vector{ + {0.5f, 0.7f} + })); +} + +void ObjImporterTest::unsupportedOptionalPositionCoordinate() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "optionalCoordinates.obj"))); + const Int id = importer.mesh3DForName("UnsupportedPositionW"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): homogeneous coordinates are not supported\n"); +} + +void ObjImporterTest::unsupportedOptionalTextureCoordinate() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "optionalCoordinates.obj"))); + const Int id = importer.mesh3DForName("UnsupportedTextureW"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): 3D texture coordinates are not supported\n"); +} + +void ObjImporterTest::shortFloatData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("ShortFloat"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): invalid float array size\n"); +} + +void ObjImporterTest::longFloatData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("LongFloat"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): invalid float array size\n"); +} + +void ObjImporterTest::longOptionalFloatData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("LongOptionalFloat"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): invalid float array size\n"); +} + +void ObjImporterTest::longIndexData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("InvalidIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): invalid index data\n"); +} + +void ObjImporterTest::wrongPointIndexData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("WrongPointIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): wrong index count for point\n"); +} + +void ObjImporterTest::wrongLineIndexData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("WrongLineIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): wrong index count for line\n"); +} + +void ObjImporterTest::wrongTriangleIndexData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("WrongTriangleIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): wrong index count for triangle\n"); +} + +void ObjImporterTest::polygonIndexData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongNumberCount.obj"))); + const Int id = importer.mesh3DForName("PolygonIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): polygons are not supported\n"); +} + +void ObjImporterTest::missingPositionData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "missingData.obj"))); + const Int id = importer.mesh3DForName("MissingPositionData"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): incomplete position data\n"); +} + +void ObjImporterTest::missingPositionIndices() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "missingData.obj"))); + const Int id = importer.mesh3DForName("MissingPositionIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): incomplete position data\n"); +} + +void ObjImporterTest::missingNormalData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "missingData.obj"))); + const Int id = importer.mesh3DForName("MissingNormalData"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): incomplete normal data\n"); +} + +void ObjImporterTest::missingNormalIndices() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "missingData.obj"))); + const Int id = importer.mesh3DForName("MissingNormalIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): incomplete normal data\n"); +} + +void ObjImporterTest::missingTextureCoordinateData() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "missingData.obj"))); + const Int id = importer.mesh3DForName("MissingTextureData"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): incomplete texture coordinate data\n"); +} + +void ObjImporterTest::missingTextureCoordinateIndices() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "missingData.obj"))); + const Int id = importer.mesh3DForName("MissingTextureIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): incomplete texture coordinate data\n"); +} + +void ObjImporterTest::wrongNormalIndexCount() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongIndexCount.obj"))); + const Int id = importer.mesh3DForName("ShortNormalIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): some normal indices are missing\n"); +} + +void ObjImporterTest::wrongTextureCoordinateIndexCount() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "wrongIndexCount.obj"))); + const Int id = importer.mesh3DForName("ShortTextureIndices"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): some texture coordinate indices are missing\n"); +} + +void ObjImporterTest::unsupportedKeyword() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "keywords.obj"))); + const Int id = importer.mesh3DForName("UnsupportedKeyword"); + CORRADE_VERIFY(id > -1); + + /* Everything should be parsed properly */ + const std::optional data = importer.mesh3D(id); + CORRADE_VERIFY(data); + CORRADE_COMPARE(data->primitive(), MeshPrimitive::Points); + CORRADE_COMPARE(data->positionArrayCount(), 1); + CORRADE_COMPARE(data->positions(0), (std::vector{ + {0.0f, 1.0f, 2.0f} + })); + CORRADE_COMPARE(data->indices(), std::vector{0}); +} + +void ObjImporterTest::unknownKeyword() { + ObjImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(OBJIMPORTER_TEST_DIR, "keywords.obj"))); + const Int id = importer.mesh3DForName("UnknownKeyword"); + CORRADE_VERIFY(id > -1); + + std::ostringstream out; + Error::setOutput(&out); + CORRADE_VERIFY(!importer.mesh3D(id)); + CORRADE_COMPARE(out.str(), "Trade::ObjImporter::mesh3D(): unknown keyword bleh\n"); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Trade::Test::ObjImporterTest) diff --git a/src/MagnumPlugins/ObjImporter/Test/configure.h.cmake b/src/MagnumPlugins/ObjImporter/Test/configure.h.cmake new file mode 100644 index 000000000..484448999 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/configure.h.cmake @@ -0,0 +1,26 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + 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. +*/ + +#define OBJIMPORTER_TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}" diff --git a/src/MagnumPlugins/ObjImporter/Test/emptyFile.obj b/src/MagnumPlugins/ObjImporter/Test/emptyFile.obj new file mode 100644 index 000000000..2abea3a74 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/emptyFile.obj @@ -0,0 +1 @@ +# Nothinng to see here diff --git a/src/MagnumPlugins/ObjImporter/Test/keywords.obj b/src/MagnumPlugins/ObjImporter/Test/keywords.obj new file mode 100644 index 000000000..c93dce27f --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/keywords.obj @@ -0,0 +1,7 @@ +o UnsupportedKeyword +g VertexGroup +v 0 1 2 +p 1 + +o UnknownKeyword +bleh diff --git a/src/MagnumPlugins/ObjImporter/Test/lineMesh.obj b/src/MagnumPlugins/ObjImporter/Test/lineMesh.obj new file mode 100644 index 000000000..d6eae8940 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/lineMesh.obj @@ -0,0 +1,8 @@ +# Positions +v 0.5 2 3 +v 0 1.5 1 +v 2 3 5.0 + +# Lines +l 1 2 +l 2 3 diff --git a/src/MagnumPlugins/ObjImporter/Test/missingData.obj b/src/MagnumPlugins/ObjImporter/Test/missingData.obj new file mode 100644 index 000000000..554d520b9 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/missingData.obj @@ -0,0 +1,23 @@ +o MissingPositionData +p 1 + +o MissingPositionIndices +v 1 2 3 + +o MissingNormalData +v 1 2 3 +p 3//3 + +o MissingNormalIndices +v 1 2 3 +vn 1 2 3 +p 4 + +o MissingTextureData +v 1 2 3 +p 5/1 + +o MissingTextureIndices +v 1 2 3 +vt 1 2 +p 6 diff --git a/src/MagnumPlugins/ObjImporter/Test/mixedPrimitives.obj b/src/MagnumPlugins/ObjImporter/Test/mixedPrimitives.obj new file mode 100644 index 000000000..e4ab55130 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/mixedPrimitives.obj @@ -0,0 +1,13 @@ +# Positions +v 0.5 2 3 +v 0 1.5 1 +v 2 3 5.0 + +# Points +p 1 +p 3 +p 2 + +# Lines +l 1 2 +l 2 3 diff --git a/src/MagnumPlugins/ObjImporter/Test/moreMeshes.obj b/src/MagnumPlugins/ObjImporter/Test/moreMeshes.obj new file mode 100644 index 000000000..e4820d5b9 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/moreMeshes.obj @@ -0,0 +1,27 @@ +# Points +o PointMesh +v 0.5 2 3 +v 0 1.5 1 +vn 0.5 2 3 +vn 0 1.5 1 +p 1//1 +p 2//2 + +# Lines +o LineMesh +v 0.5 2 3 +v 0 1.5 1 +vt 0.5 2 +vt 0 1.5 +l 3/1 4/2 +l 4/2 3/1 + +# Triangles +o TriangleMesh +v 0.5 2 3 +v 0 1.5 1 +v 2 3 5.5 +vt 0.5 2 +vn 0.5 2 3 +f 5/3/3 6/3/3 7/3/3 +f 7/3/3 6/3/3 5/3/3 diff --git a/src/MagnumPlugins/ObjImporter/Test/namedMesh.obj b/src/MagnumPlugins/ObjImporter/Test/namedMesh.obj new file mode 100644 index 000000000..16802830f --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/namedMesh.obj @@ -0,0 +1,2 @@ +# Named mesh +o MyMesh diff --git a/src/MagnumPlugins/ObjImporter/Test/normals.obj b/src/MagnumPlugins/ObjImporter/Test/normals.obj new file mode 100644 index 000000000..15711d5ca --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/normals.obj @@ -0,0 +1,12 @@ +# Positions +v 0.5 2 3 +v 0 1.5 1 + +# Normals (don't have to be normalized) +vn 1 0.5 3.5 +vn 0.5 1 0.5 + +# Lines +l 1//1 2//1 +l 1//2 2//2 +l 2//1 1//1 diff --git a/src/MagnumPlugins/ObjImporter/Test/optionalCoordinates.obj b/src/MagnumPlugins/ObjImporter/Test/optionalCoordinates.obj new file mode 100644 index 000000000..7e9132465 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/optionalCoordinates.obj @@ -0,0 +1,17 @@ +o SupportedPositionW +v 1.5 2 3 1.0 +p 1 + +o SupportedTextureW +v 1.5 2 3 +vt 0.5 0.7 0.0 +p 2/1 + +o UnsupportedPositionW +v 1.5 2 3 0.8 +p 3 + +o UnsupportedTextureW +v 1.5 2 3 +vt 0.5 0.7 0.5 +p 4/2 diff --git a/src/MagnumPlugins/ObjImporter/Test/pointMesh.obj b/src/MagnumPlugins/ObjImporter/Test/pointMesh.obj new file mode 100644 index 000000000..c5991619c --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/pointMesh.obj @@ -0,0 +1,10 @@ +# Positions +v 0.5 2 3 +v 0 1.5 1 +v 2 3 5.0 + +# Points +p 1 +p 3 +p 2 +p 1 diff --git a/src/MagnumPlugins/ObjImporter/Test/textureCoordinates.obj b/src/MagnumPlugins/ObjImporter/Test/textureCoordinates.obj new file mode 100644 index 000000000..ae72e239c --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/textureCoordinates.obj @@ -0,0 +1,12 @@ +# Positions +v 0.5 2 3 +v 0 1.5 1 + +# Texture coordinates +vt 1 0.5 +vt 0.5 1 + +# Lines +l 1/1 2/1 +l 1/2 2/2 +l 2/1 1/1 diff --git a/src/MagnumPlugins/ObjImporter/Test/textureCoordinatesNormals.obj b/src/MagnumPlugins/ObjImporter/Test/textureCoordinatesNormals.obj new file mode 100644 index 000000000..c1750ecb2 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/textureCoordinatesNormals.obj @@ -0,0 +1,17 @@ +# Positions +v 0.5 2 3 +v 0 1.5 1 + +# Texture coordinates +vt 1 0.5 +vt 0.5 1 + +# Normals +vn 1 0.5 3.5 +vn 0.5 1 0.5 + +# Lines +l 1/1/1 2/1/2 +l 1/2/2 2/2/1 +l 2/1/2 1/1/1 +l 2/2/2 1/2/2 diff --git a/src/MagnumPlugins/ObjImporter/Test/triangleMesh.obj b/src/MagnumPlugins/ObjImporter/Test/triangleMesh.obj new file mode 100644 index 000000000..32fb5196b --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/triangleMesh.obj @@ -0,0 +1,9 @@ +# Positions +v 0.5 2 3 +v 0 1.5 1 +v 2 3 5.0 +v 2.5 0 1 + +# Triangles +f 1 2 3 +f 4 2 1 diff --git a/src/MagnumPlugins/ObjImporter/Test/unnamedFirstMesh.obj b/src/MagnumPlugins/ObjImporter/Test/unnamedFirstMesh.obj new file mode 100644 index 000000000..71752c6d9 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/unnamedFirstMesh.obj @@ -0,0 +1,2 @@ +v 1 2 3 +o SecondMesh diff --git a/src/MagnumPlugins/ObjImporter/Test/wrongIndexCount.obj b/src/MagnumPlugins/ObjImporter/Test/wrongIndexCount.obj new file mode 100644 index 000000000..0a98d1573 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/wrongIndexCount.obj @@ -0,0 +1,13 @@ +o ShortNormalIndices +v 1 2 3 +vn 1 2 3 +p 1//1 +p 1 +p 1//1 + +o ShortTextureIndices +v 1 2 3 +vt 1 2 +p 2/2 +p 2 +p 2/2 diff --git a/src/MagnumPlugins/ObjImporter/Test/wrongNumberCount.obj b/src/MagnumPlugins/ObjImporter/Test/wrongNumberCount.obj new file mode 100644 index 000000000..4bfab647f --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/wrongNumberCount.obj @@ -0,0 +1,29 @@ +o ShortFloat +v 0.5 1.0 + +o LongFloat +v 0.5 1 2 +vn 0.5 1.0 2.3 7.4 + +o LongOptionalFloat +v 0.5 1 2 0.0 3.5 + +o InvalidIndices +v 1 2 3 +p 4/1/1/1 + +o WrongPointIndices +v 1 2 3 +p 5 5 + +o WrongLineIndices +v 1 2 3 +l 6 + +o WrongTriangleIndices +v 1 2 3 +f 7 7 + +o PolygonIndices +v 1 2 3 +f 8 8 8 8 diff --git a/src/MagnumPlugins/ObjImporter/Test/wrongNumbers.obj b/src/MagnumPlugins/ObjImporter/Test/wrongNumbers.obj new file mode 100644 index 000000000..ce6589a05 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/Test/wrongNumbers.obj @@ -0,0 +1,22 @@ +o WrongFloat +v 1 bleh 2 +p 1 + +o WrongInteger +v 1 0 2 +p bleh + +o PositionIndexOutOfRange +v 1 0 2 +# Should be 3 +p 1 + +o TextureIndexOutOfRange +v 1 0 2 +vt 0 1 +# Should be 4/1 +p 4/2 + +o ZeroIndex +v 1 0 2 +p 0 diff --git a/src/MagnumPlugins/ObjImporter/pluginRegistration.cpp b/src/MagnumPlugins/ObjImporter/pluginRegistration.cpp new file mode 100644 index 000000000..4ded696b1 --- /dev/null +++ b/src/MagnumPlugins/ObjImporter/pluginRegistration.cpp @@ -0,0 +1,29 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014 + 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/ObjImporter/ObjImporter.h" + +CORRADE_PLUGIN_REGISTER(ObjImporter, Magnum::Trade::ObjImporter, + "cz.mosra.magnum.Trade.AbstractImporter/0.3") diff --git a/src/MagnumPlugins/TgaImageConverter/CMakeLists.txt b/src/MagnumPlugins/TgaImageConverter/CMakeLists.txt index 5d0f72443..d0f03e2b8 100644 --- a/src/MagnumPlugins/TgaImageConverter/CMakeLists.txt +++ b/src/MagnumPlugins/TgaImageConverter/CMakeLists.txt @@ -32,7 +32,7 @@ set(TgaImageConverter_HEADERS add_library(TgaImageConverterObjects OBJECT ${TgaImageConverter_SRCS}) set_target_properties(TgaImageConverterObjects PROPERTIES COMPILE_FLAGS "-DTgaImageConverterObjects_EXPORTS ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") -add_plugin(TgaImageConverter ${MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR} +add_plugin(TgaImageConverter ${MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_INSTALL_DIR} ${MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_INSTALL_DIR} TgaImageConverter.conf $ pluginRegistration.cpp) diff --git a/src/MagnumPlugins/TgaImporter/CMakeLists.txt b/src/MagnumPlugins/TgaImporter/CMakeLists.txt index 6d214d263..05633463d 100644 --- a/src/MagnumPlugins/TgaImporter/CMakeLists.txt +++ b/src/MagnumPlugins/TgaImporter/CMakeLists.txt @@ -33,7 +33,7 @@ set(TgaImporter_HEADERS add_library(TgaImporterObjects OBJECT ${TgaImporter_SRCS}) set_target_properties(TgaImporterObjects PROPERTIES COMPILE_FLAGS "-DTgaImporterObjects_EXPORTS ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") -add_plugin(TgaImporter ${MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR} +add_plugin(TgaImporter ${MAGNUM_PLUGINS_IMPORTER_DEBUG_INSTALL_DIR} ${MAGNUM_PLUGINS_IMPORTER_RELEASE_INSTALL_DIR} TgaImporter.conf $ pluginRegistration.cpp) diff --git a/src/MagnumPlugins/WavAudioImporter/CMakeLists.txt b/src/MagnumPlugins/WavAudioImporter/CMakeLists.txt index cf984a9f9..1ba650e7a 100644 --- a/src/MagnumPlugins/WavAudioImporter/CMakeLists.txt +++ b/src/MagnumPlugins/WavAudioImporter/CMakeLists.txt @@ -35,9 +35,11 @@ set(WavAudioImporter_HEADERS WavImporter.h) add_library(WavAudioImporterObjects OBJECT ${WavAudioImporter_SRCS}) -set_target_properties(WavAudioImporterObjects PROPERTIES COMPILE_FLAGS "-DWavAudioImporterObjects_EXPORTS ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") +if(NOT BUILD_STATIC OR BUILD_STATIC_PIC) + set_target_properties(WavAudioImporterObjects PROPERTIES COMPILE_FLAGS "${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") +endif() -add_plugin(WavAudioImporter ${MAGNUM_PLUGINS_AUDIOIMPORTER_INSTALL_DIR} +add_plugin(WavAudioImporter ${MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_INSTALL_DIR} ${MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_INSTALL_DIR} WavAudioImporter.conf $ pluginRegistration.cpp) diff --git a/toolchains b/toolchains index 749095394..ac87e416a 160000 --- a/toolchains +++ b/toolchains @@ -1 +1 @@ -Subproject commit 7490953943d6f23e7971b1b5076426dceb3c62eb +Subproject commit ac87e416a0e8052cde520426aa83e007d1e0e391