Browse Source

Merge branch 'master' into compatibility

Conflicts:
	src/Mesh.cpp
	src/Text/AbstractFontConverter.cpp
Vladimír Vondruš 13 years ago
parent
commit
f45d452b6b
  1. 2
      .gitignore
  2. 1
      CMakeLists.txt
  3. 9
      CONTRIBUTING.md
  4. 12
      Doxyfile
  5. 49
      README.md
  6. 6
      doc/best-practices.dox
  7. 123
      doc/building.dox
  8. 3
      doc/cmake.dox
  9. BIN
      doc/getting-started-blue.png
  10. 202
      doc/getting-started.dox
  11. BIN
      doc/getting-started.png
  12. 15
      doc/mainpage.dox
  13. 20
      doc/namespaces.dox
  14. 1
      doc/shapes.dox
  15. 2
      doc/types.dox
  16. 42
      modules/FindMagnum.cmake
  17. 25
      package/archlinux/magnum-git/PKGBUILD
  18. 10
      src/AbstractResourceLoader.h
  19. 97
      src/Audio/AbstractImporter.cpp
  20. 163
      src/Audio/AbstractImporter.h
  21. 49
      src/Audio/Audio.cpp
  22. 41
      src/Audio/Audio.h
  23. 44
      src/Audio/Buffer.cpp
  24. 120
      src/Audio/Buffer.h
  25. 55
      src/Audio/CMakeLists.txt
  26. 65
      src/Audio/Context.cpp
  27. 95
      src/Audio/Context.h
  28. 46
      src/Audio/Renderer.cpp
  29. 202
      src/Audio/Renderer.h
  30. 123
      src/Audio/Source.cpp
  31. 596
      src/Audio/Source.h
  32. 76
      src/Audio/Test/AbstractImporterTest.cpp
  33. 51
      src/Audio/Test/BufferTest.cpp
  34. 33
      src/Audio/Test/CMakeLists.txt
  35. 51
      src/Audio/Test/RendererTest.cpp
  36. 51
      src/Audio/Test/SourceTest.cpp
  37. 1
      src/Audio/Test/file.bin
  38. 25
      src/Audio/Test/testConfigure.h.cmake
  39. 35
      src/Audio/magnumAudioVisibility.h
  40. 2
      src/Buffer.h
  41. 12
      src/CMakeLists.txt
  42. 2
      src/Context.h
  43. 2
      src/DebugTools/CMakeLists.txt
  44. 116
      src/DebugTools/Implementation/CapsuleRenderer.cpp
  45. 66
      src/DebugTools/Implementation/CapsuleRenderer.h
  46. 106
      src/DebugTools/Implementation/CapsuleRendererTransformation.h
  47. 61
      src/DebugTools/Implementation/CylinderRenderer.cpp
  48. 60
      src/DebugTools/Implementation/CylinderRenderer.h
  49. 88
      src/DebugTools/Implementation/CylinderRendererTransformation.h
  50. 14
      src/DebugTools/Implementation/ForceRendererTransformation.h
  51. 2
      src/DebugTools/Implementation/SphereRenderer.cpp
  52. 3
      src/DebugTools/ResourceManager.cpp
  53. 6
      src/DebugTools/ResourceManager.h
  54. 14
      src/DebugTools/ShapeRenderer.cpp
  55. 2
      src/DebugTools/Test/CMakeLists.txt
  56. 177
      src/DebugTools/Test/CapsuleRendererTest.cpp
  57. 125
      src/DebugTools/Test/CylinderRendererTest.cpp
  58. 27
      src/DebugTools/Test/ForceRendererTest.cpp
  59. 2
      src/Framebuffer.h
  60. 1
      src/Magnum.h
  61. 45
      src/Math/Matrix3.h
  62. 50
      src/Math/Matrix4.h
  63. 31
      src/Math/Test/Matrix3Test.cpp
  64. 31
      src/Math/Test/Matrix4Test.cpp
  65. 40
      src/Math/instantiation.cpp
  66. 108
      src/Mesh.cpp
  67. 37
      src/Mesh.h
  68. 6
      src/MeshTools/CMakeLists.txt
  69. 43
      src/MeshView.cpp
  70. 139
      src/MeshView.h
  71. 77
      src/Primitives/Capsule.cpp
  72. 30
      src/Primitives/Capsule.h
  73. 24
      src/Primitives/Cylinder.cpp
  74. 8
      src/Primitives/Cylinder.h
  75. 92
      src/Primitives/Test/CapsuleTest.cpp
  76. 6
      src/Primitives/Test/CylinderTest.cpp
  77. 5
      src/Renderer.h
  78. 17
      src/ResourceManager.h
  79. 6
      src/SceneGraph/CMakeLists.txt
  80. 2
      src/Shapes/CMakeLists.txt
  81. 3
      src/Shapes/Capsule.cpp
  82. 10
      src/Shapes/Capsule.h
  83. 1
      src/Shapes/Composition.h
  84. 58
      src/Shapes/Cylinder.cpp
  85. 117
      src/Shapes/Cylinder.h
  86. 7
      src/Shapes/Implementation/CollisionDispatch.cpp
  87. 5
      src/Shapes/Plane.cpp
  88. 3
      src/Shapes/Plane.h
  89. 4
      src/Shapes/Shapes.h
  90. 15
      src/Shapes/Sphere.cpp
  91. 8
      src/Shapes/Sphere.h
  92. 1
      src/Shapes/Test/CMakeLists.txt
  93. 13
      src/Shapes/Test/CapsuleTest.cpp
  94. 85
      src/Shapes/Test/CylinderTest.cpp
  95. 10
      src/Shapes/Test/PlaneTest.cpp
  96. 16
      src/Shapes/Test/SphereTest.cpp
  97. 2
      src/Shapes/shapeImplementation.cpp
  98. 43
      src/Shapes/shapeImplementation.h
  99. 1
      src/Test/AbstractOpenGLTester.h
  100. 21
      src/Test/ResourceManagerTest.cpp
  101. Some files were not shown because too many files have changed in this diff Show More

2
.gitignore vendored

@ -1,4 +1,4 @@
build* build*/
pkg pkg
*.kdev4 *.kdev4
*~ *~

1
CMakeLists.txt

@ -37,6 +37,7 @@ cmake_dependent_option(TARGET_GLES2 "Build for OpenGL ES 2" ON "TARGET_GLES" OFF
cmake_dependent_option(TARGET_DESKTOP_GLES "Build for OpenGL ES on desktop" OFF "TARGET_GLES" OFF) cmake_dependent_option(TARGET_DESKTOP_GLES "Build for OpenGL ES on desktop" OFF "TARGET_GLES" OFF)
# Parts of the library # Parts of the library
option(WITH_AUDIO "Build Audio library" ON)
option(WITH_DEBUGTOOLS "Build DebugTools library" ON) 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" ON)
cmake_dependent_option(WITH_PRIMITIVES "Builf Primitives library" ON "NOT WITH_DEBUGTOOLS" ON) cmake_dependent_option(WITH_PRIMITIVES "Builf Primitives library" ON "NOT WITH_DEBUGTOOLS" ON)

9
CONTRIBUTING.md

@ -53,7 +53,8 @@ Code contribution
Contact Contact
------- -------
- Website - http://mosra.cz/blog/ * Website - http://mosra.cz/blog/magnum.php
- GitHub - https://github.com/mosra/magnum * GitHub - https://github.com/mosra/magnum
- E-mail - mosra@centrum.cz * Twitter - https://twitter.com/czmosra
- Jabber - mosra@jabbim.cz * E-mail - mosra@centrum.cz
* Jabber - mosra@jabbim.cz

12
Doxyfile

@ -142,7 +142,8 @@ STRIP_FROM_PATH = ../
# are normally passed to the compiler using the -I flag. # are normally passed to the compiler using the -I flag.
STRIP_FROM_INC_PATH = ../magnum/src \ STRIP_FROM_INC_PATH = ../magnum/src \
../magnum-plugins/src ../magnum-plugins/src \
../magnum-integration/src
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
# (but less readable) file names. This can be useful if your file system # (but less readable) file names. This can be useful if your file system
@ -224,7 +225,9 @@ ALIASES = \
"requires_gles20=@xrefitem requires-gles20 \"Requires OpenGL ES 2.0\" \"Functionality requiring OpenGL ES 2.0 (not available in ES 3.0 and desktop OpenGL)\"" \ "requires_gles20=@xrefitem requires-gles20 \"Requires OpenGL ES 2.0\" \"Functionality requiring OpenGL ES 2.0 (not available in ES 3.0 and desktop OpenGL)\"" \
"requires_es_extension=@xrefitem requires-es-extension \"Requires OpenGL ES extension\" \"Functionality requiring specific OpenGL ES extension\"" \ "requires_es_extension=@xrefitem requires-es-extension \"Requires OpenGL ES extension\" \"Functionality requiring specific OpenGL ES extension\"" \
"es_extension{2}=<a href=\"http://www.khronos.org/registry/gles/extensions/\1/\1_\2.txt\"><tt>\1_\2</tt></a>" \ "es_extension{2}=<a href=\"http://www.khronos.org/registry/gles/extensions/\1/\1_\2.txt\"><tt>\1_\2</tt></a>" \
"es_extension2{3}=<a href=\"http://www.khronos.org/registry/gles/extensions/\1/\3.txt\"><tt>\1_\2</tt></a>" "es_extension2{3}=<a href=\"http://www.khronos.org/registry/gles/extensions/\1/\3.txt\"><tt>\1_\2</tt></a>" \
"fn_al{1}=`al\1()`" \
"def_al{1}=`AL_\1`"
# This tag can be used to specify a number of word-keyword mappings (TCL only). # This tag can be used to specify a number of word-keyword mappings (TCL only).
# A mapping has the form "name=value". For example adding # A mapping has the form "name=value". For example adding
@ -686,6 +689,8 @@ INPUT = src/ \
doc/ \ doc/ \
../magnum-plugins/src/ \ ../magnum-plugins/src/ \
../magnum-plugins/doc/ \ ../magnum-plugins/doc/ \
../magnum-integration/src/ \
../magnum-integration/doc/ \
../magnum-examples/doc/ ../magnum-examples/doc/
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
@ -770,7 +775,8 @@ EXAMPLE_RECURSIVE = NO
# directories that contain image that are included in the documentation (see # directories that contain image that are included in the documentation (see
# the \image command). # the \image command).
IMAGE_PATH = ../magnum-examples/src/ IMAGE_PATH = doc/ \
../magnum-examples/src/
# The INPUT_FILTER tag can be used to specify a program that doxygen should # The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program # invoke to filter for each input file. Doxygen will invoke the filter program

49
README.md

@ -37,7 +37,7 @@ DESIGN GOALS
SUPPORTED PLATFORMS SUPPORTED PLATFORMS
=================== ===================
* **OpenGL** 2.1 through 4.3, core profile functionality and modern * **OpenGL** 2.1 through 4.4, core profile functionality and modern
extensions extensions
* **OpenGL ES** 2.0, 3.0 and extensions to match desktop OpenGL functionality * **OpenGL ES** 2.0, 3.0 and extensions to match desktop OpenGL functionality
* **Linux** and embedded Linux (natively using GLX/EGL and Xlib or through * **Linux** and embedded Linux (natively using GLX/EGL and Xlib or through
@ -72,13 +72,14 @@ more comprehensive guide for building, packaging and crosscompiling.
Minimal dependencies Minimal dependencies
-------------------- --------------------
* C++ compiler with good C++11 support. Currently there are two compilers * C++ compiler with good C++11 support. Currently there are two compilers
which are tested to support everything needed: **GCC** >= 4.6 and **Clang** which are tested to support everything needed: **GCC** >= 4.6 and **Clang**
>= 3.1. >= 3.1. On Windows you can use **MinGW**, Visual Studio compiler still
* **CMake** >= 2.8.8 (needed for `OBJECT` library target) lacks some needed features.
* **GLEW** - OpenGL extension wrangler (only if targeting desktop OpenGL) * **CMake** >= 2.8.8
* **Corrade** - Plugin management and utility library. You can get it at * **GLEW** - OpenGL extension wrangler (only if targeting desktop OpenGL)
https://github.com/mosra/corrade. * **Corrade** - Plugin management and utility library. You can get it at
https://github.com/mosra/corrade.
Compilation, installation Compilation, installation
------------------------- -------------------------
@ -121,26 +122,40 @@ in root directory (i.e. where `Doxyfile` is). Resulting HTML documentation
will be in `build/doc/` directory. You might need to create `build/` directory will be in `build/doc/` directory. You might need to create `build/` directory
if it doesn't exist yet. if it doesn't exist yet.
PLUGINS AND EXAMPLES GETTING STARTED
==================== ===============
Various importer plugins for image and 3D model formats are maintained in The Doxygen documentation has a thorough [guide how to start using Magnum](http://mosra.cz/blog/magnum-doc/getting-started.html)
in your project.
RELATED PROJECTS
================
The engine itself is kept as small as possible with only little dependencies.
Additional functionality, often depending on external libraries, is provided in
separate repositories. Integration with various external math and physics
libraries can be found at https://github.com/mosra/magnum-integration. Various
importer plugins for image, audio and 3D model formats are maintained in
separate repository, which can be found at https://github.com/mosra/magnum-plugins. separate repository, which can be found at https://github.com/mosra/magnum-plugins.
There are also examples of engine usage, varying from simple *Hello World*-like There are also examples of engine usage, varying from simple *Hello World*-like
example to more advanced applications, such as viewer for complex 3D models. example to more advanced applications, such as viewer for complex 3D models.
Example repository is at https://github.com/mosra/magnum-examples. Example repository is at https://github.com/mosra/magnum-examples.
Repository with bootstrap projects for many use cases, helping you get up and
running in no time is located at https://github.com/mosra/magnum-bootstrap.
CONTACT CONTACT
======= =======
Want to learn more about the library? Found a bug or want to tell me an Want to learn more about the library? Found a bug or want to tell me an awesome
awesome idea? Feel free to visit my website or contact me at: idea? Feel free to visit my website or contact me at:
* Website - http://mosra.cz/blog/ * Website - http://mosra.cz/blog/magnum.php
* GitHub - https://github.com/mosra/magnum * GitHub - https://github.com/mosra/magnum
* E-mail - mosra@centrum.cz * Twitter - https://twitter.com/czmosra
* Jabber - mosra@jabbim.cz * E-mail - mosra@centrum.cz
* Jabber - mosra@jabbim.cz
LICENSE LICENSE
======= =======

6
doc/best-practices.dox

@ -65,6 +65,12 @@ to desired target, either in constructor or using Buffer::setTargetHint().
To ease up the development, @ref Mesh checks proper target hint when adding To ease up the development, @ref Mesh checks proper target hint when adding
vertex and index buffers. vertex and index buffers.
@section best-practices-hw Hardware-specific
@subsection best-practices-intel Intel hardware
- [Performance tuning applications for Intel Graphics for Linux and Chrome OS](http://software.intel.com/sites/default/files/Performance-tuning-applications-for-Intel-GEN-Graphics-for-Linux-and-Google-Chrome-OS.pdf) [PDF]
@subsection best-practices-powervr PowerVR hardware @subsection best-practices-powervr PowerVR hardware
- [PowerVR Performance Recommendations](http://www.imgtec.com/powervr/insider/docs/PowerVR.Performance%20Recommendations.1.0.28.External.pdf) [PDF] - [PowerVR Performance Recommendations](http://www.imgtec.com/powervr/insider/docs/PowerVR.Performance%20Recommendations.1.0.28.External.pdf) [PDF]

123
doc/building.dox

@ -38,8 +38,9 @@ Minimal set of tools and libraries required for building is:
- C++ compiler with good C++11 support. Currently there are two compilers - C++ compiler with good C++11 support. Currently there are two compilers
which are tested to support everything needed: **GCC** >= 4.6 and **Clang** which are tested to support everything needed: **GCC** >= 4.6 and **Clang**
>= 3.1. >= 3.1. On Windows you can use **MinGW**, Visual Studio compiler still lacks
- **CMake** >= 2.8.8 (needed for `OBJECT` library target) some needed features.
- **CMake** >= 2.8.8
- **GLEW** - OpenGL extension wrangler (only if targeting desktop OpenGL) - **GLEW** - OpenGL extension wrangler (only if targeting desktop OpenGL)
- **Corrade** - Plugin management and utility library. See - **Corrade** - Plugin management and utility library. See
@ref building-corrade "Corrade download and installation guide" for more @ref building-corrade "Corrade download and installation guide" for more
@ -63,9 +64,10 @@ subdirectory.
@section building-compilation Compilation, installation @section building-compilation Compilation, installation
The library (for example with support for GLUT applications) can be built and @subsection building-linux Via command-line (on Linux/Unix)
installed using these four commands. See below for more information about
optional features. 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 -p build && cd build
cmake .. \ cmake .. \
@ -74,17 +76,68 @@ optional features.
make make
make install make install
The libraries are build as shared by default, pass `-DBUILD_STATIC=ON` to build The library provides a lot of CMake options (described in sections later). They
them as static. If you plan them to use with shared libraries later, enable can be passed to CMake either as `-Dname=value` parameters on command-line
also position-independent code with `-DBUILD_STATIC_PIC=ON`. If you want to (like above) or set conveniently using `cmake-gui`:
build with another compiler (e.g. Clang), pass `-DCMAKE_CXX_COMPILER=clang++`
to CMake. cd build
cmake-gui .
@subsection building-windows Using QtCreator and CMake GUI (on Windows)
On Windows, if you don't want to touch the command-line, the easiest way is to
install QtCreator (just QtCreator, you don't need the full Qt SDK) and
configure it to use MinGW and CMake.
For most convenient usage it's best to install (or copy/paste) all library
dependencies into directory where MinGW is installed (e.g. `C:/MinGW/`),
following proper filesystem hierarchy, i.e. headers into `include/` and
binaries into `bin/` or `lib/`. CMake will then have no problem finding them
and you won't need to explicitly specify path to each one.
Then just open project's root `CMakeLists.txt` file within QtCreator. It then
asks you where to create build directory, allows you to specify initial CMake
parameters and then you can just press Configure and everything should be ready
to be built. You might need to set some CMake parameters before configuring,
they can be set with `-Dname=value`. See below for more information.
After the initial import you might want to reconfigure some CMake variables
(more information below). Start CMake GUI, point it to the recently created
build dir, modify the variables and press Generate. QtCreator will detect the
changes and reparse the project accordingly.
For most convenient usage it's best to set `CMAKE_INSTALL_PREFIX` to directory
where MinGW is installed (e.g. `C:/MinGW/`) and add `C:/MinGW/bin` and
`C:/MinGW/lib` to `PATH`. Installation to given prefix can be then done from
within QtCreator by adding new `make install` build rule.
@subsection building-optional Enabling or disabling features @subsubsection building-windows-troubleshooting Windows troubleshooting
If CMake isn't able to find dependencies (e.g. Corrade is not found) and you
have installed them to MinGW directory, point to `CMAKE_FIND_ROOT_PATH` to
MinGW installation prefix, e.g. specify `-DCMAKE_FIND_ROOT_PATH=C:/MinGW/`
CMake parameter.
If building fails with GLEW linking errors (`undefined reference to glew...`),
you have to set `GLEW_LIBRARY_DLL` and `GLEW_LIBRARY_LIB` CMake variables
manually. One of them should point to `glew32.dll` and the other to
`glew32.lib`. CMake currently isn't able to distinguish between them and in
most cases points both to the same location.
See also Corrade's @ref building-corrade-windows-troubleshooting "troubleshooting section".
@subsection building-features Enabling or disabling features
The libraries are build as shared by default. If you are developing for
platform which doesn't support shared libraries or if you just want to link
them statically, enable `BUILD_STATIC` to build the libraries as static. If you
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.
By default the engine is built for desktop OpenGL. Using `TARGET_*` CMake By default the engine is built for desktop OpenGL. Using `TARGET_*` CMake
parameters you can target other platforms. Note that some features are available parameters you can target other platforms. Note that some features are
for desktop OpenGL only, see @ref requires-gl. available for desktop OpenGL only, see @ref requires-gl.
- `TARGET_GLES` - Target OpenGL ES. - `TARGET_GLES` - Target OpenGL ES.
- `TARGET_GLES2` - Target OpenGL ES 2.0. Currently enabled by default when - `TARGET_GLES2` - Target OpenGL ES 2.0. Currently enabled by default when
@ -92,10 +145,16 @@ for desktop OpenGL only, see @ref requires-gl.
- `TARGET_DESKTOP_GLES` - Target OpenGL ES on desktop, i.e. use OpenGL ES - `TARGET_DESKTOP_GLES` - Target OpenGL ES on desktop, i.e. use OpenGL ES
emulation in desktop OpenGL library. Might not be supported in all drivers. emulation in desktop OpenGL library. Might not be supported in all drivers.
By default the engine is built with everything except application libraries (see The features used can be conveniently detected in depending projects both in
below). Using `WITH_*` CMake parameters you can specify which parts will be built CMake and C++ sources, see @ref cmake and @ref src/Magnum.h for more
and which not: information. See also @ref corrade-cmake and @ref src/Corrade.h for additional
information.
By default the engine is built with everything except application libraries
(see below). Using `WITH_*` CMake parameters you can specify which parts will
be built and which not:
- `WITH_AUDIO` - Audio library. Requires **OpenAL** library.
- `WITH_DEBUGTOOLS` - DebugTools library. Enables also building of MeshTools, - `WITH_DEBUGTOOLS` - DebugTools library. Enables also building of MeshTools,
Primitives, SceneGraph, Shaders and Shapes libraries. Primitives, SceneGraph, Shaders and Shapes libraries.
- `WITH_MESHTOOLS` - MeshTools library. Enabled automatically if `WITH_DEBUGTOOLS` - `WITH_MESHTOOLS` - MeshTools library. Enabled automatically if `WITH_DEBUGTOOLS`
@ -134,13 +193,15 @@ platform best:
@subsection building-tests Building and running unit tests @subsection building-tests Building and running unit tests
If you want to build also unit tests (which are not built by default), pass If you want to build also unit tests (which are not built by default), enable
`-DBUILD_TESTS=ON` to CMake. Unit tests use Corrade's @ref Corrade::TestSuite `BUILD_TEST` in CMake. Unit tests use Corrade's @ref Corrade::TestSuite
"TestSuite" framework and can be run using "TestSuite" framework and can be run either manually (the binaries are located
in `Test/` subdirectories of build directory) or using
ctest --output-on-failure ctest --output-on-failure
in build directory. Everything should pass ;-) in build directory. On Windows the tests require the library to be installed
with DLLs accessible through `PATH`. See above for more information.
@subsection building-doc Building documentation @subsection building-doc Building documentation
@ -155,23 +216,31 @@ in root directory (i.e. where `Doxyfile` is). Resulting HTML documentation
will be in `build/doc/` directory. You might need to create `build/` directory will be in `build/doc/` directory. You might need to create `build/` directory
if it doesn't exist yet. if it doesn't exist yet.
@section building-related Related projects
The engine itself is kept as small as possible with only little dependencies.
Additional functionality, often depending on external libraries, is provided in
separate repositories. Various importer plugins for image, audio and 3D model
formats are maintained in @ref building-plugins "Plugins repository",
Integration with various external math and physics libraries is provided by
@ref building-integration "Integration library".
@section building-arch Building ArchLinux packages @section building-arch Building ArchLinux packages
In `package/archlinux` directory is currently one PKGBUILD for Git development In `package/archlinux` directory is currently one PKGBUILD for Git development
build. The package is also in AUR under the same name. build. The package is also in AUR under the same name.
There is also development PKGBUILD and MinGW development PKGBUILD in root, There are also a few development PKGBUILDs in project root, which allow you to
which allows you to build and install the package directly from source tree build and install the package directly from source tree without downloading
without downloading anything. The PKGBUILD also contains `check()` function anything. The native PKGBUILD also contains `check()` function which will run
which will run all unit tests before packaging. Note that the unit tests all unit tests before packaging.
require Qt, as said above.
If you want to build with another compiler (e.g. Clang), run makepkg this way: If you want to build with another compiler (e.g. Clang), run makepkg this way:
CXX=clang++ makepkg CXX=clang++ makepkg
Both development PKGBUILDs can detect when Clang is used and remove Development PKGBUILDs can detect when Clang is used and remove unsupported CXX
unsupported CXX flags. flags.
@section building-win Crosscompiling for Windows using MinGW @section building-win Crosscompiling for Windows using MinGW

3
doc/cmake.dox

@ -51,6 +51,7 @@ components. The base library depends on %Corrade, OpenGL and GLEW libraries (or
OpenGL ES libraries). Additional dependencies are specified by the components. OpenGL ES libraries). Additional dependencies are specified by the components.
The optional components are: The optional components are:
- `%Audio` -- Audio library (depends on OpenAL library)
- `%DebugTools` -- DebugTools library (depends on `%MeshTools`, - `%DebugTools` -- DebugTools library (depends on `%MeshTools`,
`%Primitives`, `%SceneGraph`, `%Shaders` and `%Shapes` components) `%Primitives`, `%SceneGraph`, `%Shaders` and `%Shapes` components)
- `%MeshTools` -- MeshTools library - `%MeshTools` -- MeshTools library
@ -105,6 +106,8 @@ are also available as preprocessor variables if including Magnum.h:
%Corrade library provides also its own set of CMake macros and variables, see %Corrade library provides also its own set of CMake macros and variables, see
@ref corrade-cmake "its documentation" for more information. @ref corrade-cmake "its documentation" for more information.
@ref cmake-plugins "Plugins repository" and @ref cmake-integration "Integration library"
have also their own CMake modules.
*/ */
} }

BIN
doc/getting-started-blue.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

202
doc/getting-started.dox

@ -0,0 +1,202 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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.
*/
namespace Magnum {
/** @page getting-started Getting started
@brief Get started with %Magnum in matter of minutes.
@tableofcontents
@section getting-started-download Download, build and install Magnum
Get latest version from GitHub and install it. Read full guide on
@ref building "how to download, build and install Magnum" on platform of your
choice. For our first project we will use GLUT toolkit, don't forget to enable
it for building using `WITH_GLUTAPPLICATION` CMake parameter.
@section getting-started-bootstrap Download bootstrap project
Setting up a new project can be pretty gruesome and nobody likes repeating the
same process every time. %Magnum provides "bootstrap" project structures for
many use cases, helping you get up and running in no time.
The [bootstrap repository](https://github.com/mosra/magnum-bootstrap) is
located on GitHub. The `master` branch contains just an README file and the
actual bootstrap projects are in various other branches, each covering some
particular use case. For your first project you would need the `base` branch,
which contains only the essential files you need. Download the branch [as an
archive](https://github.com/mosra/magnum-bootstrap/archive/base.zip) and
extract it somewhere. Do it rather than cloning the full repository, as it's
better to init your own repository from scratch to avoid having the history
polluted.
@section getting-started-review Review project structure
The base project consists of just seven files in two subfolders. %Magnum uses
CMake build system, see @ref cmake for more information.
modules/FindCorrade.cmake
modules/FindMagnum.cmake
modules/FindGLEW.cmake
src/MyApplication.cpp
src/CMakeLists.txt
CMakeLists.txt
.gitignore
In root there is pre-filled `.gitignore` for your Git project and also
project-wide `CMakeLists.txt`. It just sets up project name, specifies module
directory and delegates everything important to `CMakeLists.txt` in `src/`
subdirectory.
@code
cmake_minimum_required(VERSION 2.8.8)
project(MyApplication)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/modules/")
add_subdirectory(src)
@endcode
Directory `modules/` contains CMake modules for finding the needed
dependencies. Unlike modules for finding e.g. GLUT and OpenGL, which are part
of standard CMake installation, these aren't part of it and thus must be
distributed with the project. These files are just verbatim copied from %Magnum
repository.
Directory `src/` contains the actual project. To keep things simple, the
project consists of just one source file with the most minimal code possible:
@code
#include <Platform/GlutApplication.h>
#include <DefaultFramebuffer.h>
using namespace Magnum;
class MyApplication: public Platform::Application {
public:
explicit MyApplication(const Arguments& arguments);
protected:
void viewportEvent(const Vector2i& size) override;
void drawEvent() override;
};
MyApplication::MyApplication(const Arguments& arguments): Platform::Application(arguments) {}
void MyApplication::viewportEvent(const Vector2i& size) {
defaultFramebuffer.setViewport({{}, size});
}
void MyApplication::drawEvent() {
defaultFramebuffer.clear(FramebufferClear::Color);
swapBuffers();
}
MAGNUM_APPLICATION_MAIN(MyApplication)
@endcode
The application essentially does nothing, just clears properly sized screen
framebuffer to default (black) color and then does buffer swap to actually
display it on the screen. `CMakeLists.txt` finds %Magnum, sets up compiler
flags, creates the executable and links it to all needed libraries:
@code
find_package(Magnum REQUIRED GlutApplication)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CORRADE_CXX_FLAGS}")
include_directories(${MAGNUM_INCLUDE_DIRS} ${MAGNUM_APPLICATION_INCLUDE_DIRS})
add_executable(MyApplication MyApplication.cpp)
target_link_libraries(MyApplication
${MAGNUM_LIBRARIES}
${MAGNUM_APPLICATION_LIBRARIES})
@endcode
In the following tutorials the code will be explained more thoroughly.
@section getting-started-build Build it and run
In Linux (and other Unix-based OSs) you can build the example using the
following three commands: create out-of-source build directory, run cmake in it
and then run make. The application binary will then appear in src/ subdirectory
of build dir:
mkdir -p build && cd build
cmake ..
make
./src/MyApplication
On Windows, if you don't want to touch the command-line, the easiest way is to
open root `CMakeLists.txt` in QtCreator, let it import the project and then
just build and run the application. If CMake isn't able to find the
dependencies or the building fails for some reason, you might want to look at
@ref building-windows-troubleshooting.
@image html getting-started.png
@image latex getting-started.png
Now you can try to change something in the code. Without going too deep into
the concepts of graphics programming, we can change clear color to something
else and also print basic information about the GPU the engine is running on.
First include the needed headers:
@code
#include <Color.h>
#include <Context.h>
#include <Renderer.h>
@endcode
And in the constructor (which is currently empty) change the clear color and
print something to debug output:
@code
Renderer::setClearColor({0.07f, 0.44f, 0.73f});
Debug() << "Hello! This application is running on" << Context::current()->version()
<< "using" << Context::current()->rendererString();
@endcode
After rebuilding and starting the application, the clear color changes to
blueish one and something like this would be printed to the console:
@code
Hello! This application is running on OpenGL 3.3 using Geforce GT 330M
@endcode
@image html getting-started-blue.png
@image latex getting-started-blue.png
@section getting-started-tutorials Follow tutorials and learn the principles
Now that you have your first application up and running, the best way to
continue is to render your first triangle in @ref example-index "step-by-step tutorial".
Then you can dig deeper and try other examples, read about
@ref features "fundamental principles" in the documentation and start
experimenting on your own!
@section getting-started-more Additional information
- @subpage building
- @subpage building-plugins
- @subpage building-integration
- @subpage cmake
- @subpage cmake-plugins
- @subpage cmake-integration
*/
}

BIN
doc/getting-started.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

15
doc/mainpage.dox

@ -62,7 +62,7 @@ namespace Magnum {
@section mainpage-platforms Supported platforms @section mainpage-platforms Supported platforms
- **OpenGL** 2.1 through 4.3, core profile functionality and modern - **OpenGL** 2.1 through 4.4, core profile functionality and modern
extensions extensions
- **OpenGL ES** 2.0, 3.0 and extensions to match desktop OpenGL functionality - **OpenGL ES** 2.0, 3.0 and extensions to match desktop OpenGL functionality
- **Linux** and embedded Linux (natively using GLX/EGL and Xlib or through - **Linux** and embedded Linux (natively using GLX/EGL and Xlib or through
@ -83,16 +83,10 @@ namespace Magnum {
- Pre-made shaders, primitives and other tools for easy prototyping and - Pre-made shaders, primitives and other tools for easy prototyping and
debugging. debugging.
@section mainpage-download-build Downloading and building Magnum
Guide @ref building "how to download and build Magnum" on different platforms.
@section mainpage-getting-started Getting started @section mainpage-getting-started Getting started
The best way to get started is to render your first triangle in Read thorough @ref getting-started "guide to download, build, install and start using Magnum"
@ref example-index "step-by-step tutorial". Then you can dig deeper and try in your project.
other examples, read about @ref features "fundamental principles" in the
documentation or start experimenting on your own!
@section mainpage-hacking Hacking Magnum @section mainpage-hacking Hacking Magnum
@ -105,8 +99,9 @@ make the library as consistent and maintainable as possible.
Feel free to get more information or contact the author at: Feel free to get more information or contact the author at:
- Website - http://mosra.cz/blog/magnum.php
- GitHub - https://github.com/mosra/magnum - GitHub - https://github.com/mosra/magnum
- Website - http://mosra.cz/blog/ - Twitter - https://twitter.com/czmosra
- E-mail - mosra@centrum.cz - E-mail - mosra@centrum.cz
- Jabber - mosra@jabbim.cz - Jabber - mosra@jabbim.cz

20
doc/namespaces.dox

@ -88,6 +88,20 @@ This library is built by default and found by default in CMake. See
@ref building and @ref cmake for more information. @ref building and @ref cmake for more information.
*/ */
/** @dir Audio
* @brief Namespace Magnum::Audio
*/
/** @namespace Magnum::Audio
@brief %Audio playback
Audio import, playback and integration with @ref SceneGraph.
This library is built when `WITH_AUDIO` is enabled and found as `%Audio`
component in CMake. See @ref building and @ref cmake for more information.
Additional plugins are part of plugin repository, see @ref building-plugins and
@ref cmake-plugins for more information.
*/
/** @dir DebugTools /** @dir DebugTools
* @brief Namespace Magnum::DebugTools * @brief Namespace Magnum::DebugTools
*/ */
@ -177,6 +191,8 @@ Font texture creation and text layouting.
This library is built when `WITH_TEXT` is enabled and found as `%Text` This library is built when `WITH_TEXT` is enabled and found as `%Text`
component in CMake. See @ref building and @ref cmake for more information. component in CMake. See @ref building and @ref cmake for more information.
Additional plugins are part of plugin repository, see @ref building-plugins and
@ref cmake-plugins for more information.
*/ */
/** @dir TextureTools /** @dir TextureTools
@ -202,5 +218,7 @@ Contains plugin interfaces for importing data of various formats and classes
for direct access to the data. for direct access to the data.
This library is built by default and found by default in CMake. See This library is built by default and found by default in CMake. See
@ref building and @ref cmake for more information. @ref building and @ref cmake for more information. Additional plugins are part
of plugin repository, see @ref building-plugins and @ref cmake-plugins for more
information.
*/ */

1
doc/shapes.dox

@ -51,6 +51,7 @@ line and point. Collision of two lines can be detected only in 2D.
@subsection shapes-3D Three-dimensional shapes @subsection shapes-3D Three-dimensional shapes
- @ref Shapes::Sphere "Shapes::Sphere*D" -- @copybrief Shapes::Sphere - @ref Shapes::Sphere "Shapes::Sphere*D" -- @copybrief Shapes::Sphere
- @ref Shapes::Cylinder "Shapes::Cylinder*D" -- @copybrief Shapes::Cylinder
- @ref Shapes::Capsule "Shapes::Capsule*D" -- @copybrief Shapes::Capsule - @ref Shapes::Capsule "Shapes::Capsule*D" -- @copybrief Shapes::Capsule
- @ref Shapes::AxisAlignedBox "Shapes::AxisAlignedBox*D" -- @copybrief Shapes::AxisAlignedBox - @ref Shapes::AxisAlignedBox "Shapes::AxisAlignedBox*D" -- @copybrief Shapes::AxisAlignedBox
- @ref Shapes::Box "Shapes::Box*D" -- @copybrief Shapes::Box - @ref Shapes::Box "Shapes::Box*D" -- @copybrief Shapes::Box

2
doc/types.dox

@ -69,7 +69,7 @@ type, `i` is @ref Int underlying type and `d` is for @ref Double underlying type
| --------------------------------- | ------------------------------------ | | --------------------------------- | ------------------------------------ |
| @ref Matrix2 or @ref Matrix2d | `mat2`/`mat2x2` or `dmat2`/`dmat2x2` | | @ref Matrix2 or @ref Matrix2d | `mat2`/`mat2x2` or `dmat2`/`dmat2x2` |
| @ref Matrix3 or @ref Matrix3d | `mat3`/`mat3x3` or `dmat3`/`dmat3x3` | | @ref Matrix3 or @ref Matrix3d | `mat3`/`mat3x3` or `dmat3`/`dmat3x3` |
| @ref Matrix4 or @ref Matrix4d | `mat4`/`mat4x4` or `dmat3`/`dmat4x4` | | @ref Matrix4 or @ref Matrix4d | `mat4`/`mat4x4` or `dmat4`/`dmat4x4` |
| @ref Matrix2x3 or @ref Matrix2x3d | `mat2x3` or `dmat2x3` | | @ref Matrix2x3 or @ref Matrix2x3d | `mat2x3` or `dmat2x3` |
| @ref Matrix3x2 or @ref Matrix3x2d | `mat3x2` or `dmat3x2` | | @ref Matrix3x2 or @ref Matrix3x2d | `mat3x2` or `dmat3x2` |
| @ref Matrix2x4 or @ref Matrix2x4d | `mat2x4` or `dmat2x4` | | @ref Matrix2x4 or @ref Matrix2x4d | `mat2x4` or `dmat2x4` |

42
modules/FindMagnum.cmake

@ -7,14 +7,20 @@
# MAGNUM_LIBRARIES - Magnum library and dependent libraries # MAGNUM_LIBRARIES - Magnum library and dependent libraries
# MAGNUM_INCLUDE_DIRS - Root include dir and include dirs of # MAGNUM_INCLUDE_DIRS - Root include dir and include dirs of
# dependencies # dependencies
# MAGNUM_PLUGINS_DIR - Base directory with plugins. 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_FONT_DIR - Directory with font plugins
# MAGNUM_PLUGINS_FONTCONVERTER_DIR - Directory with font converter plugins # MAGNUM_PLUGINS_FONTCONVERTER_DIR - Directory with font converter plugins
# MAGNUM_PLUGINS_IMAGECONVERTER_DIR - Directory with image converter plugins # MAGNUM_PLUGINS_IMAGECONVERTER_DIR - Directory with image converter plugins
# MAGNUM_PLUGINS_IMPORTER_DIR - Directory with importer plugins # MAGNUM_PLUGINS_IMPORTER_DIR - Directory with importer plugins
# MAGNUM_PLUGINS_AUDIOIMPORTER_DIR - Directory with audio importer plugins
# This command will try to find only the base library, not the optional # This command will try to find only the base library, not the optional
# components. The base library depends on Corrade, OpenGL and GLEW # components. The base library depends on Corrade, OpenGL and GLEW
# libraries. Additional dependencies are specified by the components. The # libraries. Additional dependencies are specified by the components. The
# optional components are: # optional components are:
# Audio - Audio library (depends on OpenAL library)
# DebugTools - DebugTools library (depends on MeshTools, Primitives, # DebugTools - DebugTools library (depends on MeshTools, Primitives,
# SceneGraph, Shaders and Shapes components) # SceneGraph, Shaders and Shapes components)
# MeshTools - MeshTools library # MeshTools - MeshTools library
@ -70,6 +76,8 @@
# installation directory # installation directory
# MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR - Importer plugin installation # MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR - Importer plugin installation
# directory # directory
# MAGNUM_PLUGINS_AUDIOIMPORTER_INSTALL_DIR - Audio omporter plugin
# installation directory
# MAGNUM_CMAKE_MODULE_INSTALL_DIR - Installation dir for CMake # MAGNUM_CMAKE_MODULE_INSTALL_DIR - Installation dir for CMake
# modules # modules
# MAGNUM_INCLUDE_INSTALL_DIR - Header installation directory # MAGNUM_INCLUDE_INSTALL_DIR - Header installation directory
@ -215,6 +223,19 @@ foreach(component ${Magnum_FIND_COMPONENTS})
endif() endif()
endif() endif()
# Audio library
if(${component} STREQUAL Audio)
set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES Audio.h)
find_package(OpenAL)
if(OPENAL_FOUND)
set(_MAGNUM_${_COMPONENT}_LIBRARIES ${OPENAL_LIBRARY})
set(_MAGNUM_${_COMPONENT}_INCLUDE_DIRS ${OPENAL_INCLUDE_DIR})
else()
unset(MAGNUM_${_COMPONENT}_LIBRARY)
endif()
endif()
# DebugTools library # DebugTools library
if(${component} STREQUAL DebugTools) if(${component} STREQUAL DebugTools)
set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES DebugTools.h) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES DebugTools.h)
@ -325,6 +346,7 @@ 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_FONTCONVERTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/fontconverters)
set(MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/imageconverters) 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_IMPORTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/importers)
set(MAGNUM_PLUGINS_AUDIOIMPORTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/audioimporters)
set(MAGNUM_CMAKE_MODULE_INSTALL_DIR ${CMAKE_ROOT}/Modules) set(MAGNUM_CMAKE_MODULE_INSTALL_DIR ${CMAKE_ROOT}/Modules)
set(MAGNUM_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include/Magnum) set(MAGNUM_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include/Magnum)
set(MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include/Magnum/Plugins) set(MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include/Magnum/Plugins)
@ -337,19 +359,17 @@ mark_as_advanced(FORCE
MAGNUM_PLUGINS_FONTCONVERTER_INSTALL_DIR MAGNUM_PLUGINS_FONTCONVERTER_INSTALL_DIR
MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR
MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR
MAGNUM_PLUGINS_AUDIOIMPORTER_INSTALL_DIR
MAGNUM_CMAKE_MODULE_INSTALL_DIR MAGNUM_CMAKE_MODULE_INSTALL_DIR
MAGNUM_INCLUDE_INSTALL_DIR MAGNUM_INCLUDE_INSTALL_DIR
MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR) MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR)
set(MAGNUM_PLUGINS_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}
CACHE PATH "Base directory where to look for Magnum plugins")
# Plugin directories # Plugin directories
if(NOT WIN32) set(MAGNUM_PLUGINS_FONT_DIR ${MAGNUM_PLUGINS_DIR}/fonts)
set(MAGNUM_PLUGINS_FONT_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/fonts) set(MAGNUM_PLUGINS_FONTCONVERTER_DIR ${MAGNUM_PLUGINS_DIR}/fontconverters)
set(MAGNUM_PLUGINS_FONTCONVERTER_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/fontconverters) set(MAGNUM_PLUGINS_IMAGECONVERTER_DIR ${MAGNUM_PLUGINS_DIR}/imageconverters)
set(MAGNUM_PLUGINS_IMAGECONVERTER_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/imageconverters) set(MAGNUM_PLUGINS_IMPORTER_DIR ${MAGNUM_PLUGINS_DIR}/importers)
set(MAGNUM_PLUGINS_IMPORTER_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/importers) set(MAGNUM_PLUGINS_AUDIOIMPORTER_DIR ${MAGNUM_PLUGINS_DIR}/audioimporters)
else()
set(MAGNUM_PLUGINS_FONT_DIR fonts)
set(MAGNUM_PLUGINS_FONTCONVERTER_DIR fontconverters)
set(MAGNUM_PLUGINS_IMAGECONVERTER_DIR imageconverters)
set(MAGNUM_PLUGINS_IMPORTER_DIR importers)
endif()

25
package/archlinux/magnum-git/PKGBUILD

@ -1,17 +1,23 @@
# Author: mosra <mosra@centrum.cz> # Author: mosra <mosra@centrum.cz>
pkgname=magnum-git pkgname=magnum-git
pkgver=20120331 pkgver=20130819
pkgrel=1 pkgrel=1
pkgdesc="OpenGL 3 graphics engine (Git version)" pkgdesc="C++11 and OpenGL 2D/3D graphics engine (Git version)"
arch=('i686' 'x86_64') arch=('i686' 'x86_64')
url="https://github.com/mosra/magnum" url="http://mosra.cz/blog/magnum.php"
license=('MIT') license=('MIT')
depends=('corrade-git' 'glew') depends=('corrade-git' 'glew' 'glut' 'openal')
makedepends=('cmake' 'git') makedepends=('cmake' 'git')
provides=('magnum')
conflicts=('magnum')
_gitroot="git://github.com/mosra/magnum.git" _gitroot="git://github.com/mosra/magnum.git"
_gitname="magnum" _gitname="magnum"
pkgver() {
date +%Y%m%d
}
build() { build() {
cd "$srcdir" cd "$srcdir"
msg "Connecting to Git server..." msg "Connecting to Git server..."
@ -31,15 +37,14 @@ build() {
cmake ../$_gitname \ cmake ../$_gitname \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_PREFIX=/usr \
-DWITH_GLUTAPPLICATION=ON \
-DWITH_GLXAPPLICATION=ON \
-DWITH_WINDOWLESSGLXAPPLICATION=ON \
-DWITH_MAGNUMINFO=ON
make make
} }
check() {
cd "$startdir/build"
ctest --output-on-failure -E Benchmark
}
package() { package() {
cd "$srcdir/build" cd "$srcdir/build"
make DESTDIR="$pkgdir/" install make DESTDIR="$pkgdir/" install

10
src/AbstractResourceLoader.h

@ -161,6 +161,11 @@ template<class T> class AbstractResourceLoader {
*/ */
void set(ResourceKey key, T* data, ResourceDataState state, ResourcePolicy policy); void set(ResourceKey key, T* data, ResourceDataState state, ResourcePolicy policy);
/** @overload */
template<class U> void set(ResourceKey key, U&& data, ResourceDataState state, ResourcePolicy policy) {
set(key, new typename std::remove_cv<typename std::remove_reference<U>::type>::type(std::forward<U>(data)), state, policy);
}
/** /**
* @brief Set loaded resource to resource manager * @brief Set loaded resource to resource manager
* *
@ -171,6 +176,11 @@ template<class T> class AbstractResourceLoader {
set(key, data, ResourceDataState::Final, ResourcePolicy::Resident); set(key, data, ResourceDataState::Final, ResourcePolicy::Resident);
} }
/** @overload */
template<class U> void set(ResourceKey key, U&& data) {
set(key, new typename std::remove_cv<typename std::remove_reference<U>::type>::type(std::forward<U>(data)));
}
/** /**
* @brief Mark resource as not found * @brief Mark resource as not found
* *

97
src/Audio/AbstractImporter.cpp

@ -0,0 +1,97 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 "AbstractImporter.h"
#include <fstream>
#include <Containers/Array.h>
#include <Utility/Assert.h>
namespace Magnum { namespace Audio {
AbstractImporter::AbstractImporter() = default;
AbstractImporter::AbstractImporter(PluginManager::AbstractManager* manager, std::string plugin): PluginManager::AbstractPlugin(manager, std::move(plugin)) {}
bool AbstractImporter::openData(Containers::ArrayReference<const unsigned char> data) {
CORRADE_ASSERT(features() & Feature::OpenData,
"Audio::AbstractImporter::openData(): feature not supported", nullptr);
close();
doOpenData(data);
return isOpened();
}
void AbstractImporter::doOpenData(Containers::ArrayReference<const unsigned char>) {
CORRADE_ASSERT(false, "Audio::AbstractImporter::openData(): feature advertised but not implemented", );
}
bool AbstractImporter::openFile(const std::string& filename) {
close();
doOpenFile(filename);
return isOpened();
}
void AbstractImporter::doOpenFile(const std::string& filename) {
CORRADE_ASSERT(features() & Feature::OpenData, "Audio::AbstractImporter::openFile(): not implemented", );
/* Open file */
std::ifstream in(filename.data(), std::ios::binary);
if(!in.good()) {
Error() << "Trade::AbstractImporter::openFile(): cannot open file" << filename;
return;
}
/* Create array to hold file contents */
in.seekg(0, std::ios::end);
Containers::Array<unsigned char> data(in.tellg());
/* Read data, close */
in.seekg(0, std::ios::beg);
in.read(reinterpret_cast<char*>(data.begin()), data.size());
in.close();
doOpenData(data);
}
void AbstractImporter::close() {
if(isOpened()) doClose();
}
Buffer::Format AbstractImporter::format() const {
CORRADE_ASSERT(isOpened(), "Audio::AbstractImporter::format(): no file opened", {});
return doFormat();
}
UnsignedInt AbstractImporter::frequency() const {
CORRADE_ASSERT(isOpened(), "Audio::AbstractImporter::frequency(): no file opened", {});
return doFrequency();
}
Containers::Array<unsigned char> AbstractImporter::data() {
CORRADE_ASSERT(isOpened(), "Audio::AbstractImporter::data(): no file opened", {});
return doData();
}
}}

163
src/Audio/AbstractImporter.h

@ -0,0 +1,163 @@
#ifndef Magnum_Audio_AbstractImporter_h
#define Magnum_Audio_AbstractImporter_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 Audio/AbstractImporter.h
* @brief Class Magnum::Audio::AbstractImporter
*/
#include <PluginManager/AbstractPlugin.h>
#include "Magnum.h"
#include "Audio/Buffer.h"
namespace Magnum { namespace Audio {
/**
@brief Base for audio importer plugins
@section Audio-AbstractImporter-subclassing Subclassing
Plugin implements function doFeatures(), doIsOpened(), one of or both
doOpenData() and doOpenFile() functions, function doClose() and data access
functions doFormat(), doFrequency() and doData().
You don't need to do most of the redundant sanity checks, these things are
checked by the implementation:
- Functions doOpenData() and doOpenFile() are called after the previous file
was closed, function doClose() is called only if there is any file opened.
- Function doOpenData() is called only if @ref Feature "Feature::OpenData"
is supported.
- All `do*()` implementations working on opened file are called only if
there is any file opened.
*/
class MAGNUM_AUDIO_EXPORT AbstractImporter: public PluginManager::AbstractPlugin {
CORRADE_PLUGIN_INTERFACE("cz.mosra.magnum.Audio.AbstractImporter/0.1")
public:
/**
* @brief Features supported by this importer
*
* @see Features, features()
*/
enum class Feature: UnsignedByte {
/** Opening files from raw data using openData() */
OpenData = 1 << 0
};
/**
* @brief Features supported by this importer
*
* @see features()
*/
typedef Containers::EnumSet<Feature, UnsignedByte> Features;
/** @brief Default constructor */
explicit AbstractImporter();
/** @brief Plugin manager constructor */
explicit AbstractImporter(PluginManager::AbstractManager* manager, std::string plugin);
/** @brief Features supported by this importer */
Features features() const { return doFeatures(); }
/** @brief Whether any file is opened */
bool isOpened() const { return doIsOpened(); }
/**
* @brief Open raw data
*
* Closes previous file, if it was opened, and tries to open given
* file. Available only if @ref Feature "Feature::OpenData" is
* supported. Returns `true` on success, `false` otherwise.
* @see features(), openFile()
*/
bool openData(Containers::ArrayReference<const unsigned char> data);
/**
* @brief Open file
*
* Closes previous file, if it was opened, and tries to open given
* file. Returns `true` on success, `false` otherwise.
* @see features(), openData()
*/
bool openFile(const std::string& filename);
/** @brief Close file */
void close();
/** @{ @name Data access */
/** @brief Sample format */
Buffer::Format format() const;
/** @brief Sample frequency */
UnsignedInt frequency() const;
/** @brief Sample data */
Containers::Array<unsigned char> data();
/*@}*/
#ifndef DOXYGEN_GENERATING_OUTPUT
private:
#else
protected:
#endif
/** @brief Implementation for features() */
virtual Features doFeatures() const = 0;
/** @brief Implementation for isOpened() */
virtual bool doIsOpened() const = 0;
/** @brief Implementation for openData() */
virtual void doOpenData(Containers::ArrayReference<const unsigned char> data);
/**
* @brief Implementation for openFile()
*
* If @ref Feature "Feature::OpenData" is supported, default
* implementation opens the file and calls doOpenData() with its
* contents.
*/
virtual void doOpenFile(const std::string& filename);
/** @brief Implementation for close() */
virtual void doClose() = 0;
/** @brief Implementation for format() */
virtual Buffer::Format doFormat() const = 0;
/** @brief Implementation for frequency() */
virtual UnsignedInt doFrequency() const = 0;
/** @brief Implementation for data() */
virtual Containers::Array<unsigned char> doData() = 0;
};
}}
#endif

49
src/Audio/Audio.cpp

@ -0,0 +1,49 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <type_traits>
#include <al.h>
#include "Types.h"
namespace Magnum { namespace Audio {
/* Verify types */
static_assert(std::is_same<ALubyte, UnsignedByte>::value, "ALubyte is not the same as UnsignedByte");
static_assert(std::is_same<ALbyte, Byte>::value, "ALbyte is not the same as Byte");
static_assert(std::is_same<ALushort, UnsignedShort>::value, "ALushort is not the same as UnsignedShort");
static_assert(std::is_same<ALshort, Short>::value, "ALshort is not the same as Short");
static_assert(std::is_same<ALuint, UnsignedInt>::value, "ALuint is not the same as UnsignedInt");
static_assert(std::is_same<ALint, Int>::value, "ALint is not the same as Int");
static_assert(std::is_same<ALsizei, Int>::value, "ALsizei is not the same as Int");
static_assert(std::is_same<ALfloat, Float>::value, "ALfloat is not the same as Float");
#ifndef MAGNUM_TARGET_GLES
static_assert(std::is_same<ALdouble, Double>::value, "ALdouble is not the same as Double");
#endif
/* Verify boolean values */
static_assert(AL_FALSE == false, "AL_FALSE is not the same as false");
static_assert(AL_TRUE == true, "AL_TRUE is not the same as true");
}}

41
src/Audio/Audio.h

@ -0,0 +1,41 @@
#ifndef Magnum_Audio_Audio_h
#define Magnum_Audio_Audio_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 Forward declarations for Magnum::Audio namespace
*/
namespace Magnum { namespace Audio {
class AbstractImporter;
class Buffer;
class Context;
class Source;
/* Renderer used only statically */
}}
#endif

44
src/Audio/Buffer.cpp

@ -0,0 +1,44 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 "Buffer.h"
#include <Utility/Debug.h>
namespace Magnum { namespace Audio {
Debug operator<<(Debug debug, const Buffer::Format value) {
switch(value) {
#define _c(value) case Buffer::Format::value: return debug << "Audio::Buffer::Format::" #value;
_c(Mono8)
_c(Mono16)
_c(Stereo8)
_c(Stereo16)
#undef _c
}
return debug << "Audio::Buffer::Format::(invalid)";
}
}}

120
src/Audio/Buffer.h

@ -0,0 +1,120 @@
#ifndef Magnum_Audio_Buffer_h
#define Magnum_Audio_Buffer_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 Magnum::Audio::Buffer
*/
#include <utility>
#include <al.h>
#include <Containers/Array.h>
#include "Magnum.h"
#include "Audio/magnumAudioVisibility.h"
namespace Magnum { namespace Audio {
/** @brief Sample buffer */
class Buffer {
public:
/**
* @brief Sample format
*
* @note Multi-channel format is played without 3D spatialization
* (useful for background music)
* @see @ref setData()
*/
enum class Format: ALenum {
Mono8 = AL_FORMAT_MONO8, /**< 8-bit unsigned mono */
Mono16 = AL_FORMAT_MONO16, /**< 16-bit signed mono */
Stereo8 = AL_FORMAT_STEREO8, /**< 8-bit interleaved unsigned stereo */
Stereo16 = AL_FORMAT_STEREO16 /**< 16-bit interleaved signed stereo */
};
/**
* @brief Constructor
*
* Creates OpenAL buffer object.
* @see @fn_al{GenBuffers}
*/
explicit Buffer() { alGenBuffers(1, &_id); }
/**
* @brief Destructor
*
* Deletes OpenAL buffer object.
* @see @fn_al{DeleteBuffers}
*/
~Buffer() { if(_id) alDeleteBuffers(1, &_id); }
/** @brief Copying is not allowed */
Buffer(const Buffer&) = delete;
/** @brief Move constructor */
Buffer(Buffer&& other);
/** @brief Copying is not allowed */
Buffer& operator=(const Buffer&) = delete;
/** @brief Move assignment */
Buffer& operator=(Buffer&& other);
/** @brief OpenAL buffer ID */
ALuint id() const { return _id; }
/**
* @brief Set buffer data
* @param format Sample format
* @param data Data
* @param frequency Frequency
* @return Reference to self (for method chaining)
*
* @see @fn_al{BufferData}
*/
Buffer& setData(Format format, Containers::ArrayReference<const void> data, ALsizei frequency) {
alBufferData(_id, ALenum(format), data, data.size(), frequency);
return *this;
}
private:
ALuint _id;
};
/** @debugoperator{Magnum::Audio::Buffer} */
Debug MAGNUM_AUDIO_EXPORT operator<<(Debug debug, Buffer::Format value);
inline Buffer::Buffer(Buffer&& other): _id(other._id) {
other._id = 0;
}
inline Buffer& Buffer::operator=(Buffer&& other) {
std::swap(_id, other._id);
return *this;
}
}}
#endif

55
src/Audio/CMakeLists.txt

@ -0,0 +1,55 @@
#
# This file is part of Magnum.
#
# Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
find_package(OpenAL REQUIRED)
include_directories(${OPENAL_INCLUDE_DIR})
set(MagnumAudio_SOURCES
AbstractImporter.cpp
Audio.cpp
Buffer.cpp
Context.cpp
Renderer.cpp
Source.cpp)
set(MagnumAudio_HEADERS
AbstractImporter.h
Audio.h
Buffer.h
Context.h
Renderer.h
Source.h
magnumAudioVisibility.h)
add_library(MagnumAudio ${SHARED_OR_STATIC} ${MagnumAudio_SOURCES})
target_link_libraries(MagnumAudio ${CORRADE_PLUGINMANAGER_LIBRARIES} ${OPENAL_LIBRARY})
install(TARGETS MagnumAudio DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR})
install(FILES ${MagnumAudio_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Audio)
if(BUILD_TESTS)
add_subdirectory(Test)
endif()

65
src/Audio/Context.cpp

@ -0,0 +1,65 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 "Context.h"
#include <alc.h>
#include <Utility/Assert.h>
#include <Utility/Debug.h>
#include "Magnum.h"
namespace Magnum { namespace Audio {
Context* Context::_current = nullptr;
Context::Context() {
CORRADE_ASSERT(!_current, "Audio::Context: context already created", );
/* Open default device */
const ALCchar* const defaultDevice = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER);
_device = alcOpenDevice(defaultDevice);
if(!_device) {
Error() << "Audio::Context: cannot open sound device" << defaultDevice;
std::exit(1);
}
_context = alcCreateContext(_device, nullptr);
if(!_context) {
Error() << "Audio::Context: cannot create context:" << alcGetError(_device);
std::exit(1);
}
alcMakeContextCurrent(_context);
_current = this;
}
Context::~Context() {
CORRADE_INTERNAL_ASSERT(_current == this);
alcDestroyContext(_context);
alcCloseDevice(_device);
}
}}

95
src/Audio/Context.h

@ -0,0 +1,95 @@
#ifndef Magnum_Audio_Context_h
#define Magnum_Audio_Context_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 Magnum::Audio::Context
*/
#include <al.h>
#include <string>
#include "Audio/magnumAudioVisibility.h"
#ifndef DOXYGEN_GENERATING_OUTPUT
typedef struct ALCdevice_struct ALCdevice;
typedef struct ALCcontext_struct ALCcontext;
#endif
namespace Magnum { namespace Audio {
/**
@brief OpenAL context
*/
class MAGNUM_AUDIO_EXPORT Context {
public:
/** @brief Current context */
static Context* current() { return _current; }
/**
* @brief Constructor
*
* Creates OpenAL context.
*/
explicit Context();
/**
* @brief Destructor
*
* Destroys OpenAL context.
*/
~Context();
/**
* @brief Vendor string
*
* @see rendererString(), @fn_al{GetString} with @def_al{VENDOR}
*/
std::string vendorString() const { return alGetString(AL_VENDOR); }
/**
* @brief %Renderer string
*
* @see vendorString(), @fn_al{GetString} with @def_al{RENDERER}
*/
std::string rendererString() const { return alGetString(AL_RENDERER); }
/**
* @brief Version string
*
* @see @fn_al{GetString} with @def_al{VERSION}
*/
std::string versionString() const { return alGetString(AL_VERSION); }
private:
static Context* _current;
ALCdevice* _device;
ALCcontext* _context;
};
}}
#endif

46
src/Audio/Renderer.cpp

@ -0,0 +1,46 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 "Renderer.h"
#include <Utility/Debug.h>
namespace Magnum { namespace Audio {
Debug operator<<(Debug debug, const Renderer::Error value) {
switch(value) {
#define _c(value) case Renderer::Error::value: return debug << "Audio::Renderer::Error::" #value;
_c(NoError)
_c(InvalidName)
_c(InvalidEnum)
_c(InvalidValue)
_c(InvalidOperation)
_c(OutOfMemory)
#undef _c
}
return debug << "Audio::Renderer::Error::(invalid)";
}
}}

202
src/Audio/Renderer.h

@ -0,0 +1,202 @@
#ifndef Magnum_Audio_Renderer_h
#define Magnum_Audio_Renderer_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 Magnum::Audio::Renderer
*/
#include <al.h>
#include "Math/Vector3.h"
#include "Magnum.h"
#include "Audio/magnumAudioVisibility.h"
namespace Magnum { namespace Audio {
/** @brief Global renderer configuration */
class Renderer {
public:
Renderer() = delete;
/**
* @brief Error status
*
* @see error()
*/
enum class Error: ALenum {
NoError = AL_NO_ERROR, /**< No error occured */
InvalidName = AL_INVALID_NAME, /**< Invalid name parameter */
InvalidEnum = AL_INVALID_ENUM, /**< Invalid enum parameter */
InvalidValue = AL_INVALID_VALUE, /**< Invalid enum value parameter */
InvalidOperation = AL_INVALID_OPERATION, /**< Illegal call */
OutOfMemory = AL_OUT_OF_MEMORY /**< Unable to allocate memory */
};
/** @brief Error status */
static Error error() { return Error(alGetError()); }
/** @{ @name Listener positioning */
/**
* @brief Set listener position
*
* Default is `{0.0f, 0.0f, 0.0f}`.
* @see @fn_al{Listenerfv} with @def_al{POSITION}
*/
static void setListenerPosition(const Vector3& position) {
alListenerfv(AL_POSITION, position.data());
}
/** @overload
* @see @fn_al{Listeneriv} with @def_al{POSITION}
*/
static void setListenerPosition(const Vector3i& position) {
alListeneriv(AL_POSITION, position.data());
}
/**
* @brief Set listener orientation
*
* The values must be linearly independent and don't need to be
* normalized. Default is -Z and +Y.
* @see @fn_al{Listenerfv} with @def_al{ORIENTATION}
*/
static void setListenerOrientation(const Vector3& forward, const Vector3& up);
/** @overload
* @see @fn_al{Listeneriv} with @def_al{ORIENTATION}
*/
static void setListenerOrientation(const Vector3i& forward, const Vector3i& up);
/**
* @brief Set listener velocity
*
* Default is `{0.0f, 0.0f, 0.0f}`.
* @see @fn_al{Listenerfv} with @def_al{VELOCITY}
*/
static void setListenerVelocity(const Vector3& velocity) {
alListenerfv(AL_VELOCITY, velocity.data());
}
/** @overload
* @see @fn_al{Listeneriv} with @def_al{VELOCITY}
*/
static void setListenerVelocity(const Vector3i& velocity) {
alListeneriv(AL_VELOCITY, velocity.data());
}
/*@}*/
/** @{ @name Global behavior */
/**
* @brief Distance model
*
* @see setDistanceModel()
*/
enum class DistanceModel: ALenum {
/** No distance attenuation calculation */
None = AL_NONE,
/** Inverse distance */
Inverse = AL_INVERSE_DISTANCE,
/** Inverse distance, clamped */
InverseClamped = AL_INVERSE_DISTANCE_CLAMPED,
/** Linear distance */
Linear = AL_LINEAR_DISTANCE,
/** Linear distance, clamped */
LinearClamped = AL_LINEAR_DISTANCE_CLAMPED,
/** Exponential distance */
Exponent = AL_EXPONENT_DISTANCE,
/** Exponential distance, clamped */
ExponentClamped = AL_EXPONENT_DISTANCE_CLAMPED
};
/**
* @brief Set listener gain
*
* Default is `1.0f`, which means that the sound is unattenuated.
* If set to `0.0f`, all sound is muted.
* @see @fn_al{Listenerf} with @def_al{GAIN}
*/
static void setListenerGain(Float gain) {
alListenerf(AL_GAIN, gain);
}
/**
* @brief Set Doppler factor
*
* Default is `1.0f`. If set to `0.0f`, the effect is disabled.
* @see @ref setSpeedOfSound(), @fn_al{DopplerFactor}
*/
static void setDopplerFactor(Float factor) {
alDopplerFactor(factor);
}
/**
* @brief Set speed of sound
*
* Default is `343.3f` (meters per second).
* @see @ref setDopplerFactor(), @fn_al{SpeedOfSound}
*/
static void setSpeedOfSound(Float speed) {
alSpeedOfSound(speed);
}
/**
* @brief Set distance model
*
* Default is @ref DistanceModel "DistanceModel::InverseClamped".
* @see @fn_al{DistanceModel}
*/
static void setDistanceModel(DistanceModel model) {
alDistanceModel(ALenum(model));
}
/*@}*/
};
/** @debugoperator{Magnum::Audio::Renderer} */
Debug MAGNUM_AUDIO_EXPORT operator<<(Debug debug, Renderer::Error value);
inline void Renderer::setListenerOrientation(const Vector3& forward, const Vector3& up) {
const Vector3 data[] = {forward, up};
alListenerfv(AL_ORIENTATION, data[0].data());
}
inline void Renderer::setListenerOrientation(const Vector3i& forward, const Vector3i& up) {
const Vector3i data[] = {forward, up};
alListeneriv(AL_ORIENTATION, data[0].data());
}
}}
#endif

123
src/Audio/Source.cpp

@ -0,0 +1,123 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 "Source.h"
#include "Audio/Buffer.h"
namespace Magnum { namespace Audio {
/** @todo C++14: use VLA to avoid unnecessary allocations */
Source& Source::setBuffer(Buffer* buffer) {
alSourcei(_id, AL_BUFFER, buffer ? buffer->id() : 0);
return *this;
}
namespace {
const ALuint* sourceIds(const std::initializer_list<Source*>& sources) {
ALuint* const ids = new ALuint[sources.size()];
for(auto it = sources.begin(); it != sources.end(); ++it) {
CORRADE_INTERNAL_ASSERT(*it);
ids[it-sources.begin()] = (*it)->id();
}
return ids;
}
const ALuint* sourceIds(const std::vector<Source*>& sources) {
ALuint* const ids = new ALuint[sources.size()];
for(auto it = sources.begin(); it != sources.end(); ++it) {
CORRADE_INTERNAL_ASSERT(*it);
ids[it-sources.begin()] = (*it)->id();
}
return ids;
}
}
/** @todo Okay, this is too much code copying even for me */
void Source::play(std::initializer_list<Source*> sources) {
const ALuint* const ids = sourceIds(sources);
alSourcePlayv(sources.size(), ids);
delete[] ids;
}
void Source::play(const std::vector<Source*>& sources) {
const ALuint* const ids = sourceIds(sources);
alSourcePlayv(sources.size(), ids);
delete[] ids;
}
void Source::pause(std::initializer_list<Source*> sources) {
const ALuint* const ids = sourceIds(sources);
alSourcePausev(sources.size(), ids);
delete[] ids;
}
void Source::pause(const std::vector<Source*>& sources) {
const ALuint* const ids = sourceIds(sources);
alSourcePausev(sources.size(), ids);
delete[] ids;
}
void Source::stop(std::initializer_list<Source*> sources) {
const ALuint* const ids = sourceIds(sources);
alSourceStopv(sources.size(), ids);
delete[] ids;
}
void Source::stop(const std::vector<Source*>& sources) {
const ALuint* const ids = sourceIds(sources);
alSourceStopv(sources.size(), ids);
delete[] ids;
}
void Source::rewind(std::initializer_list<Source*> sources) {
const ALuint* const ids = sourceIds(sources);
alSourceRewindv(sources.size(), ids);
delete[] ids;
}
void Source::rewind(const std::vector<Source*>& sources) {
const ALuint* const ids = sourceIds(sources);
alSourceRewindv(sources.size(), ids);
delete[] ids;
}
Debug operator<<(Debug debug, const Source::State value) {
switch(value) {
#define _c(value) case Source::State::value: return debug << "Audio::Source::State::" #value;
_c(Initial)
_c(Playing)
_c(Paused)
_c(Stopped)
#undef _c
}
return debug << "Audio::Source::State::(invalid)";
}
}}

596
src/Audio/Source.h

@ -0,0 +1,596 @@
#ifndef Magnum_Audio_Source_h
#define Magnum_Audio_Source_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 Magnum::Audio::Source
*/
#include <initializer_list>
#include <vector>
#include <al.h>
#include "Math/Vector3.h"
#include "Magnum.h"
#include "Audio/Audio.h"
#include "Audio/magnumAudioVisibility.h"
namespace Magnum { namespace Audio {
/**
@brief %Source
Manages positional audio source.
@todo Expose convenient API for buffer queuing
*/
class MAGNUM_AUDIO_EXPORT Source {
public:
/**
* @brief Constructor
*
* Creates OpenAL source object.
* @see @fn_al{GenSources}
*/
explicit Source() { alGenSources(1, &_id); }
/**
* @brief Destructor
*
* Deletes OpenAL source object.
* @see @fn_al{DeleteSources}
*/
~Source() { if(_id) alDeleteSources(1, &_id); }
/** @brief Copying is not allowed */
Source(const Source&) = delete;
/** @brief Move constructor */
Source(Source&& other);
/** @brief Copying is not allowed */
Source& operator=(const Source&) = delete;
/** @brief Move assignment */
Source& operator=(Source&& other);
/** @brief OpenAL source ID */
ALuint id() const { return _id; }
/** @{ @name Source positioning */
/**
* @brief Set position
* @return Reference to self (for method chaining)
*
* Default is `{0.0f, 0.0f, 0.0f}`.
* @see @ref setRelative(), @fn_al{Sourcefv} with @def_al{POSITION}
*/
Source& setPosition(const Vector3& position) {
alSourcefv(_id, AL_POSITION, position.data());
return *this;
}
/** @overload
* @see @fn_al{Sourceiv} with @def_al{POSITION}
*/
Source& setPosition(const Vector3i& position) {
alSourceiv(_id, AL_POSITION, position.data());
return *this;
}
/**
* @brief Set velocity
* @return Reference to self (for method chaining)
*
* Default is `{0.0f, 0.0f, 0.0f}`.
* @see @ref setRelative(), @fn_al{Sourcefv} with @def_al{VELOCITY}
*/
Source& setVelocity(const Vector3& velocity) {
alSourcefv(_id, AL_VELOCITY, velocity.data());
return *this;
}
/** @overload
* @see @fn_al{Sourceiv} with @def_al{VELOCITY}
*/
Source& setVelocity(const Vector3i& velocity) {
alSourceiv(_id, AL_VELOCITY, velocity.data());
return *this;
}
/**
* @brief Interpret source relatively to listener
*
* When enabled, source position, direction and velocity will be
* interpreted relatively to listener. Default is `false`.
* @see @ref setPosition(), @ref setDirection(), @ref setVelocity(),
* @fn_al{Sourcei} with @def_al{SOURCE_RELATIVE}
*/
Source& setRelative(bool relative) {
alSourcei(_id, AL_SOURCE_RELATIVE, relative);
return *this;
}
/*@}*/
/** @{ @name Source behavior */
/**
* @brief Set gain
* @return Reference to self (for method chaining)
*
* Default is `1.0f`, which means that the sound is unattenuated.
* If set to `0.0f`, the source is muted.
* @see @ref setMinGain(), @ref setMaxGain(), @fn_al{Sourcef} with
* @def_al{GAIN}
*/
Source& setGain(Float gain) {
alSourcef(_id, AL_GAIN, gain);
return *this;
}
/**
* @brief Set min gain
* @return Reference to self (for method chaining)
*
* If effective gain is lower than min gain, min gain is used. Note
* that this is done before listener gain is applied. Default is
* `0.0f`.
* @see @ref setMinGain(), @ref setGain(), @fn_al{Sourcef} with
* @def_al{MIN_GAIN}
*/
Source& setMinGain(Float gain) {
alSourcef(_id, AL_MIN_GAIN, gain);
return *this;
}
/**
* @brief Set max gain
* @return Reference to self (for method chaining)
*
* If effective gain is higher than max gain, max gain is used. Note
* that this is done before listener gain is applied. Default is
* `1.0f`. If set to `0.0f`, the source is muted.
* @see @ref setMinGain(), @ref setGain(), @fn_al{Sourcef} with
* @def_al{MIN_GAIN}
*/
Source& setMaxGain(Float gain) {
alSourcef(_id, AL_MAX_GAIN, gain);
return *this;
}
/**
* @brief Set reference distance
* @return Reference to self (for method chaining)
*
* Default is `1.0f`.
* @see @ref setRolloffFactor(), @fn_al{Sourcef} with
* @def_al{REFERENCE_DISTANCE}
*/
Source& setReferenceDistance(Float distance) {
alSourcef(_id, AL_REFERENCE_DISTANCE, distance);
return *this;
}
/** @overload
* @see @fn_al{Sourcei} with @def_al{REFERENCE_DISTANCE}
*/
Source& setReferenceDistance(Int distance) {
alSourcei(_id, AL_REFERENCE_DISTANCE, distance);
return *this;
}
/**
* @brief Set rolloff factor
* @return Reference to self (for method chaining)
*
* Default is `1.0f`.
* @see @ref setReferenceDistance(), @fn_al{Sourcef} with
* @def_al{ROLLOFF_FACTOR}
*/
Source& setRolloffFactor(Float factor) {
alSourcef(_id, AL_ROLLOFF_FACTOR, factor);
return *this;
}
/** @overload
* @see @fn_al{Sourcei} with @def_al{ROLLOFF_FACTOR}
*/
Source& setRolloffFactor(Int factor) {
alSourcei(_id, AL_ROLLOFF_FACTOR, factor);
return *this;
}
/**
* @brief Set max distance
* @return Reference to self (for method chaining)
*
* Default is max representable value.
* @see @fn_al{Sourcef} with @def_al{MAX_DISTANCE}
*/
Source& setMaxDistance(Float distance) {
alSourcef(_id, AL_MAX_DISTANCE, distance);
return *this;
}
/** @overload
* @see @fn_al{Sourcei} with @def_al{MAX_DISTANCE}
*/
Source& setMaxDistance(Int distance) {
alSourcef(_id, AL_MAX_DISTANCE, distance);
return *this;
}
/**
* @brief Set direction
* @return Reference to self (for method chaining)
*
* Default is `{0.0f, 0.0f, 0.0f}`, which means that the source is not
* directional.
* @see @ref setInnerConeAngle(), @ref setOuterConeAngle(),
* @ref setRelative(), @fn_al{Sourcefv} with @def_al{DIRECTION}
*/
Source& setDirection(const Vector3& direction) {
alSourcefv(_id, AL_DIRECTION, direction.data());
return *this;
}
/** @overload
* @see @fn_al{Sourceiv} with @def_al{DIRECTION}
*/
Source& setDirection(const Vector3i& direction) {
alSourceiv(_id, AL_DIRECTION, direction.data());
return *this;
}
/**
* @brief Set inner cone angle
* @return Reference to self (for method chaining)
*
* Has effect only if the source is directional. Default is
* `360.0_degf`.
* @see @ref setOuterConeAngle(), @ref setDirection(), @fn_al{Sourcef}
* with @def_al{CONE_INNER_ANGLE}
*/
Source& setInnerConeAngle(Deg angle) {
alSourcef(_id, AL_CONE_INNER_ANGLE, Float(angle));
return *this;
}
/**
* @brief Set outer cone angle
* @return Reference to self (for method chaining)
*
* Has effect only if the source is directional. Default is
* `360.0_degf`.
* @see @ref setInnerConeAngle(), @ref setDirection(),
* @ref setOuterConeGain() @fn_al{Sourcef} with
* @def_al{CONE_OUTER_ANGLE}
*/
Source& setOuterConeAngle(Deg angle) {
alSourcef(_id, AL_CONE_OUTER_ANGLE, Float(angle));
return *this;
}
/**
* @brief Set outer cone gain multiplier
* @return Reference to self (for method chaining)
*
* The factor with which the gain is multiplied outside the outer cone.
* Default is `0.0f`.
* @see @ref setGain(), @ref setOuterConeAngle(), @fn_al{Sourcef} with
* @def_al{CONE_OUTER_GAIN}
*/
Source& setOuterConeGain(Float multiplier) {
alSourcef(_id, AL_CONE_OUTER_ANGLE, multiplier);
return *this;
}
/**
* @brief Set pitch
* @return Reference to self (for method chaining)
*
* Default is `1.0f`.
* @see @fn_al{Sourcef} with @def_al{PITCH}
*/
Source& setPitch(Float pitch) {
alSourcef(_id, AL_PITCH, pitch);
return *this;
}
/*@}*/
/** @{ @name Buffer management */
/**
* @brief %Source type
*
* @see @ref type()
*/
enum class Type: ALint {
Undetermined = AL_UNDETERMINED, /**< Undetermined (default) */
Static = AL_STATIC, /**< Static source */
Streaming = AL_STREAMING /**< Streaming source */
};
/**
* @brief Source type
*
* @see @ref setBuffer(), @fn_al{GetSourcei} with @def_al{SOURCE_TYPE}
*/
Type type() const;
/**
* @brief Attach buffer
* @param buffer Buffer to attach or `nullptr`
* @return Reference to self (for method chaining)
*
* If an buffer is attached, changes source type to
* @ref Type "Type::Static", if detached, changes source type to
* @ref Type "Type::Undetermined". The buffer must be already filled
* with data.
* @see @ref type(), @fn_al{Sourcei} with @def_al{BUFFER}
*/
Source& setBuffer(Buffer* buffer);
/*@}*/
/** @{ @name State management */
/**
* @brief %Source state
*
* @see @ref state(), @ref play(), @ref pause(), @ref stop(),
* @ref rewind()
*/
enum class State: ALint {
Initial = AL_INITIAL, /**< Initial state (default) */
Playing = AL_PLAYING, /**< The source is playing */
Paused = AL_PAUSED, /**< The source is paused */
Stopped = AL_STOPPED /**< The source is stopped */
};
/**
* @brief Play more sources at once
*
* The operation is guaranteed to be done for all sources at the same
* time. `nullptr` is not allowed.
* @see @ref play(), @ref pause(std::initializer_list<Source*>),
* @ref stop(std::initializer_list<Source*>),
* @ref rewind(std::initializer_list<Source*>),
* @fn_al{SourcePlayv}
*/
static void play(std::initializer_list<Source*> sources);
static void play(const std::vector<Source*>& sources); /**< @overload */
/**
* @brief Pause more sources at once
*
* The operation is guaranteed to be done for all sources at the same
* time. `nullptr` is not allowed.
* @see @ref pause(), @ref play(std::initializer_list<Source*>),
* @ref stop(std::initializer_list<Source*>),
* @ref rewind(std::initializer_list<Source*>),
* @fn_al{SourcePausev}
*/
static void pause(std::initializer_list<Source*> sources);
static void pause(const std::vector<Source*>& sources); /**< @overload */
/**
* @brief Stop more sources at once
*
* The operation is guaranteed to be done for all sources at the same
* time. `nullptr` is not allowed.
* @see @ref stop(), @ref play(std::initializer_list<Source*>),
* @ref pause(std::initializer_list<Source*>),
* @ref rewind(std::initializer_list<Source*>),
* @fn_al{SourceStopv}
*/
static void stop(std::initializer_list<Source*> sources);
static void stop(const std::vector<Source*>& sources); /**< @overload */
/**
* @brief Rewind more sources at once
*
* The operation is guaranteed to be done for all sources at the same
* time. `nullptr` is not allowed.
* @see @ref rewind(), @ref play(std::initializer_list<Source*>),
* @ref pause(std::initializer_list<Source*>),
* @ref stop(std::initializer_list<Source*>),
* @fn_al{SourceRewindv}
*/
static void rewind(std::initializer_list<Source*> sources);
static void rewind(const std::vector<Source*>& sources); /**< @overload */
/**
* @brief State
*
* @see @ref play(), @ref pause(), @ref stop(), @ref rewind(),
* @fn_al{GetSourcei} with @def_al{SOURCE_STATE}
*/
State state() const;
/**
* @brief Play
*
* @see @ref play(std::initializer_list<Source*>), @ref state(),
* @ref pause(), @ref stop(), @ref rewind(), @fn_al{SourcePlay}
*/
void play() { alSourcePlay(_id); }
/**
* @brief Pause
*
* @see @ref pause(std::initializer_list<Source*>), @ref state(),
* @ref play(), @ref stop(), @ref rewind(), @fn_al{SourcePause}
*/
void pause() { alSourcePause(_id); }
/**
* @brief Stop
*
* @see @ref stop(std::initializer_list<Source*>), @ref state(),
* @ref play(), @ref pause(), @ref rewind(), @fn_al{SourceStop}
*/
void stop() { alSourceStop(_id); }
/**
* @brief Rewind
*
* @see @ref rewind(std::initializer_list<Source*>), @ref state(),
* @ref play(), @ref pause(), @ref stop(), @fn_al{SourceRewind}
*/
void rewind() { alSourceRewind(_id); }
/**
* @brief Whether the source is looping
*
* @see @fn_al{GetSourcei} with @def_al{LOOPING}
*/
bool isLooping() const;
/**
* @brief Set source looping
* @return Reference to self (for method chaining)
*
* Default is `false`.
* @see @fn_al{Sourcei} with @def_al{LOOPING}
*/
Source& setLooping(bool loop) {
alSourcei(_id, AL_LOOPING, loop);
return *this;
}
/**
* @brief Offset in seconds
*
* @see @ref offsetInBytes(), @ref offsetInSamples(),
* @fn_al{GetSourcef} with @def_al{SEC_OFFSET}
*/
Float offsetInSeconds() const;
/**
* @brief Set offset in seconds
* @return Reference to self (for method chaining)
*
* @see @ref setOffsetInBytes(), @ref setOffsetInSamples(),
* @fn_al{Sourcef} with @def_al{SEC_OFFSET}
*/
Source& setOffsetInSeconds(Float offset) {
alSourcef(_id, AL_SEC_OFFSET, offset);
return *this;
}
/**
* @brief Offset in bytes
*
* @see @ref offsetInSeconds(), @ref offsetInSamples(),
* @fn_al{GetSourcei} with @def_al{BYTE_OFFSET}
*/
Int offsetInBytes() const;
/**
* @brief Set offset in bytes
* @return Reference to self (for method chaining)
*
* @see @ref setOffsetInSeconds(), @ref setOffsetInSamples(),
* @fn_al{Sourcei} with @def_al{SEC_OFFSET}
*/
Source& setOffsetInBytes(Int offset) {
alSourcei(_id, AL_BYTE_OFFSET, offset);
return *this;
}
/**
* @brief Offset in samples
*
* @see @ref offsetInSeconds(), @ref offsetInBytes(),
* @fn_al{GetSourcei} with @def_al{SAMPLE_OFFSET}
*/
Int offsetInSamples() const;
/**
* @brief Set offset in samples
* @return Reference to self (for method chaining)
*
* @see @ref setOffsetInSeconds(), @ref setOffsetInBytes(),
* @fn_al{Sourcei} with @def_al{SEC_OFFSET}
*/
Source& setOffsetInSamples(Int offset) {
alSourcei(_id, AL_SAMPLE_OFFSET, offset);
return *this;
}
/*@}*/
private:
ALuint _id;
};
/** @debugoperator{Magnum::Audio::Source} */
Debug MAGNUM_AUDIO_EXPORT operator<<(Debug debug, Source::State value);
inline Source::Source(Source&& other): _id(other._id) {
other._id = 0;
}
inline Source& Source::operator=(Source&& other) {
std::swap(_id, other._id);
return *this;
}
auto Source::state() const -> State {
ALint state;
alGetSourcei(_id, AL_SOURCE_STATE, &state);
return State(state);
}
inline bool Source::isLooping() const {
ALint looping;
alGetSourcei(_id, AL_LOOPING, &looping);
return looping;
}
inline Float Source::offsetInSeconds() const {
Float offset;
alGetSourcef(_id, AL_SEC_OFFSET, &offset);
return offset;
}
inline Int Source::offsetInBytes() const {
Int offset;
alGetSourcei(_id, AL_BYTE_OFFSET, &offset);
return offset;
}
inline Int Source::offsetInSamples() const {
Int offset;
alGetSourcei(_id, AL_SAMPLE_OFFSET, &offset);
return offset;
}
}}
#endif

76
src/Audio/Test/AbstractImporterTest.cpp

@ -0,0 +1,76 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <Containers/Array.h>
#include <TestSuite/Tester.h>
#include <Utility/Directory.h>
#include "Audio/AbstractImporter.h"
#include "testConfigure.h"
namespace Magnum { namespace Audio { namespace Test {
class AbstractImporterTest: public TestSuite::Tester {
public:
explicit AbstractImporterTest();
void openFile();
};
AbstractImporterTest::AbstractImporterTest() {
addTests({&AbstractImporterTest::openFile});
}
void AbstractImporterTest::openFile() {
class DataImporter: public Audio::AbstractImporter {
public:
explicit DataImporter(): opened(false) {}
private:
Features doFeatures() const override { return Feature::OpenData; }
bool doIsOpened() const override { return opened; }
void doClose() override {}
void doOpenData(Containers::ArrayReference<const unsigned char> data) override {
opened = (data.size() == 1 && data[0] == 0xa5);
}
Buffer::Format doFormat() const override { return {}; }
UnsignedInt doFrequency() const override { return {}; }
Corrade::Containers::Array<unsigned char> doData() override { return nullptr; }
bool opened;
};
/* doOpenFile() should call doOpenData() */
DataImporter importer;
CORRADE_VERIFY(!importer.isOpened());
importer.openFile(Utility::Directory::join(AUDIO_TEST_DIR, "file.bin"));
CORRADE_VERIFY(importer.isOpened());
}
}}}
CORRADE_TEST_MAIN(Magnum::Audio::Test::AbstractImporterTest)

51
src/Audio/Test/BufferTest.cpp

@ -0,0 +1,51 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <sstream>
#include <TestSuite/Tester.h>
#include "Audio/Buffer.h"
namespace Magnum { namespace Audio { namespace Test {
class BufferTest: public TestSuite::Tester {
public:
explicit BufferTest();
void debugFormat();
};
BufferTest::BufferTest() {
addTests({&BufferTest::debugFormat});
}
void BufferTest::debugFormat() {
std::ostringstream out;
Debug(&out) << Buffer::Format::Stereo16;
CORRADE_COMPARE(out.str(), "Audio::Buffer::Format::Stereo16\n");
}
}}}
CORRADE_TEST_MAIN(Magnum::Audio::Test::BufferTest)

33
src/Audio/Test/CMakeLists.txt

@ -0,0 +1,33 @@
#
# This file is part of Magnum.
#
# Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
#
# 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}/testConfigure.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/testConfigure.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
corrade_add_test(AudioAbstractImporterTest AbstractImporterTest.cpp LIBRARIES MagnumAudio)
corrade_add_test(AudioBufferTest BufferTest.cpp LIBRARIES MagnumAudio)
corrade_add_test(AudioRendererTest RendererTest.cpp LIBRARIES MagnumAudio)
corrade_add_test(AudioSourceTest SourceTest.cpp LIBRARIES MagnumAudio)

51
src/Audio/Test/RendererTest.cpp

@ -0,0 +1,51 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <sstream>
#include <TestSuite/Tester.h>
#include "Audio/Renderer.h"
namespace Magnum { namespace Audio { namespace Test {
class RendererTest: public TestSuite::Tester {
public:
explicit RendererTest();
void debugError();
};
RendererTest::RendererTest() {
addTests({&RendererTest::debugError});
}
void RendererTest::debugError() {
std::ostringstream out;
Debug(&out) << Renderer::Error::InvalidOperation;
CORRADE_COMPARE(out.str(), "Audio::Renderer::Error::InvalidOperation\n");
}
}}}
CORRADE_TEST_MAIN(Magnum::Audio::Test::RendererTest)

51
src/Audio/Test/SourceTest.cpp

@ -0,0 +1,51 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <sstream>
#include <TestSuite/Tester.h>
#include "Audio/Source.h"
namespace Magnum { namespace Audio { namespace Test {
class SourceTest: public TestSuite::Tester {
public:
explicit SourceTest();
void debugState();
};
SourceTest::SourceTest() {
addTests({&SourceTest::debugState});
}
void SourceTest::debugState() {
std::ostringstream out;
Debug(&out) << Source::State::Playing;
CORRADE_COMPARE(out.str(), "Audio::Source::State::Playing\n");
}
}}}
CORRADE_TEST_MAIN(Magnum::Audio::Test::SourceTest)

1
src/Audio/Test/file.bin

@ -0,0 +1 @@
<EFBFBD>

25
src/Audio/Test/testConfigure.h.cmake

@ -0,0 +1,25 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 AUDIO_TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}"

35
src/Audio/magnumAudioVisibility.h

@ -0,0 +1,35 @@
#ifndef Magnum_Audio_magnumAudioVisibility_h
#define Magnum_Audio_magnumAudioVisibility_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <Utility/Visibility.h>
#ifdef MagnumAudio_EXPORTS
#define MAGNUM_AUDIO_EXPORT CORRADE_VISIBILITY_EXPORT
#else
#define MAGNUM_AUDIO_EXPORT CORRADE_VISIBILITY_IMPORT
#endif
#endif

2
src/Buffer.h

@ -24,7 +24,7 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
/** @file /** @file /Buffer.h
* @brief Class Magnum::Buffer * @brief Class Magnum::Buffer
*/ */

12
src/CMakeLists.txt

@ -58,6 +58,7 @@ set(Magnum_SRCS
Image.cpp Image.cpp
ImageFormat.cpp ImageFormat.cpp
Mesh.cpp Mesh.cpp
MeshView.cpp
OpenGL.cpp OpenGL.cpp
Query.cpp Query.cpp
Renderbuffer.cpp Renderbuffer.cpp
@ -117,6 +118,7 @@ set(Magnum_HEADERS
ImageReference.h ImageReference.h
Magnum.h Magnum.h
Mesh.h Mesh.h
MeshView.h
OpenGL.h OpenGL.h
Query.h Query.h
Renderbuffer.h Renderbuffer.h
@ -191,6 +193,10 @@ add_subdirectory(Math)
add_subdirectory(Platform) add_subdirectory(Platform)
add_subdirectory(Trade) add_subdirectory(Trade)
if(WITH_AUDIO)
add_subdirectory(Audio)
endif()
if(WITH_DEBUGTOOLS) if(WITH_DEBUGTOOLS)
add_subdirectory(DebugTools) add_subdirectory(DebugTools)
endif() endif()
@ -236,5 +242,11 @@ if(BUILD_TESTS)
set_target_properties(MagnumTestLib PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) set_target_properties(MagnumTestLib PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT)
target_link_libraries(MagnumTestLib ${Magnum_LIBS}) target_link_libraries(MagnumTestLib ${Magnum_LIBS})
# 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 MagnumMathTestLib MagnumTestLib DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR})
endif()
add_subdirectory(Test) add_subdirectory(Test)
endif() endif()

2
src/Context.h

@ -24,7 +24,7 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
/** @file /** @file /Context.h
* @brief Enum Magnum::Version, class Magnum::Context, Magnum::Extension, macro MAGNUM_ASSERT_VERSION_SUPPORTED(), MAGNUM_ASSERT_EXTENSION_SUPPORTED() * @brief Enum Magnum::Version, class Magnum::Context, Magnum::Extension, macro MAGNUM_ASSERT_VERSION_SUPPORTED(), MAGNUM_ASSERT_EXTENSION_SUPPORTED()
*/ */

2
src/DebugTools/CMakeLists.txt

@ -33,6 +33,8 @@ set(MagnumDebugTools_SRCS
Implementation/AbstractShapeRenderer.cpp Implementation/AbstractShapeRenderer.cpp
Implementation/AxisAlignedBoxRenderer.cpp Implementation/AxisAlignedBoxRenderer.cpp
Implementation/BoxRenderer.cpp Implementation/BoxRenderer.cpp
Implementation/CapsuleRenderer.cpp
Implementation/CylinderRenderer.cpp
Implementation/LineSegmentRenderer.cpp Implementation/LineSegmentRenderer.cpp
Implementation/PointRenderer.cpp Implementation/PointRenderer.cpp
Implementation/SphereRenderer.cpp) Implementation/SphereRenderer.cpp)

116
src/DebugTools/Implementation/CapsuleRenderer.cpp

@ -0,0 +1,116 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 "CapsuleRenderer.h"
#include "MeshView.h"
#include "DebugTools/ResourceManager.h"
#include "DebugTools/ShapeRenderer.h"
#include "Primitives/Capsule.h"
#include "Shapes/Capsule.h"
#include "Shaders/Flat.h"
#include "Trade/MeshData2D.h"
#include "Trade/MeshData3D.h"
#include "DebugTools/Implementation/CapsuleRendererTransformation.h"
namespace Magnum { namespace DebugTools { namespace Implementation {
AbstractCapsuleRenderer<2>::AbstractCapsuleRenderer(): AbstractShapeRenderer<2>("capsule2d", "capsule2d-vertices", "capsule2d-indices") {
constexpr UnsignedInt rings = 10;
if(!wireframeMesh) createResources(Primitives::Capsule2D::wireframe(rings, 1, 1.0f));
/* Bottom hemisphere */
if(!(bottom = ResourceManager::instance().get<MeshView>("capsule2d-bottom"))) {
auto view = new MeshView(wireframeMesh);
view->setIndexRange(0, rings*4, 0, rings*2+1);
ResourceManager::instance().set(bottom.key(), view, ResourceDataState::Final, ResourcePolicy::Manual);
}
/* Cylinder */
if(!(cylinder = ResourceManager::instance().get<MeshView>("capsule2d-cylinder"))) {
auto view = new MeshView(wireframeMesh);
view->setIndexRange(rings*4, 4, rings*2+1, rings*2+3);
ResourceManager::instance().set(cylinder.key(), view, ResourceDataState::Final, ResourcePolicy::Manual);
}
/* Top hemisphere */
if(!(top = ResourceManager::instance().get<MeshView>("capsule2d-top"))) {
auto view = new MeshView(wireframeMesh);
view->setIndexRange(rings*4+4, rings*4, rings*2+3, rings*4+4);
ResourceManager::instance().set(top.key(), view, ResourceDataState::Final, ResourcePolicy::Manual);
}
}
AbstractCapsuleRenderer<3>::AbstractCapsuleRenderer(): AbstractShapeRenderer<3>("capsule3d", "capsule3d-vertices", "capsule3d-indices") {
constexpr UnsignedInt rings = 10;
constexpr UnsignedInt segments = 40;
if(!wireframeMesh) createResources(Primitives::Capsule3D::wireframe(rings, 1, segments, 1.0f));
/* Bottom hemisphere */
if(!(bottom = ResourceManager::instance().get<MeshView>("capsule3d-bottom"))) {
auto view = new MeshView(wireframeMesh);
view->setIndexRange(0, rings*8, 0, rings*4+1);
ResourceManager::instance().set(bottom.key(), view, ResourceDataState::Final, ResourcePolicy::Manual);
}
/* Cylinder */
if(!(cylinder = ResourceManager::instance().get<MeshView>("capsule3d-cylinder"))) {
auto view = new MeshView(wireframeMesh);
view->setIndexRange(rings*8, segments*4+8, rings*4+1, rings*4+segments*2+5);
ResourceManager::instance().set(cylinder.key(), view, ResourceDataState::Final, ResourcePolicy::Manual);
}
/* Top */
if(!(top = ResourceManager::instance().get<MeshView>("capsule3d-top"))) {
auto view = new MeshView(wireframeMesh);
view->setIndexRange(rings*8+segments*4+8, rings*8, rings*4+segments*2+5, rings*8+segments*2+6);
ResourceManager::instance().set(top.key(), view, ResourceDataState::Final, ResourcePolicy::Manual);
}
}
template<UnsignedInt dimensions> CapsuleRenderer<dimensions>::CapsuleRenderer(const Shapes::Implementation::AbstractShape<dimensions>& capsule): capsule(static_cast<const Shapes::Implementation::Shape<Shapes::Capsule<dimensions>>&>(capsule).shape) {}
template<UnsignedInt dimensions> void CapsuleRenderer<dimensions>::draw(Resource<ShapeRendererOptions>& options, const typename DimensionTraits<dimensions, Float>::MatrixType& projectionMatrix) {
std::array<typename DimensionTraits<dimensions, Float>::MatrixType, 3> transformations = Implementation::capsuleRendererTransformation<dimensions>(capsule.a(), capsule.b(), capsule.radius());
AbstractShapeRenderer<dimensions>::wireframeShader->setColor(options->color())
.use();
/* Bottom */
AbstractShapeRenderer<dimensions>::wireframeShader->setTransformationProjectionMatrix(projectionMatrix*transformations[0]);
AbstractCapsuleRenderer<dimensions>::bottom->draw();
/* Cylinder */
AbstractShapeRenderer<dimensions>::wireframeShader->setTransformationProjectionMatrix(projectionMatrix*transformations[1]);
AbstractCapsuleRenderer<dimensions>::cylinder->draw();
/* Top */
AbstractShapeRenderer<dimensions>::wireframeShader->setTransformationProjectionMatrix(projectionMatrix*transformations[2]);
AbstractCapsuleRenderer<dimensions>::top->draw();
}
template class CapsuleRenderer<2>;
template class CapsuleRenderer<3>;
}}}

66
src/DebugTools/Implementation/CapsuleRenderer.h

@ -0,0 +1,66 @@
#ifndef Magnum_DebugTools_Implementation_CapsuleRenderer_h
#define Magnum_DebugTools_Implementation_CapsuleRenderer_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 "AbstractShapeRenderer.h"
#include "Shapes/Shapes.h"
#include "corradeCompatibility.h"
namespace Magnum { namespace DebugTools { namespace Implementation {
template<UnsignedInt dimensions> class AbstractCapsuleRenderer;
template<> class AbstractCapsuleRenderer<2>: public AbstractShapeRenderer<2> {
public:
explicit AbstractCapsuleRenderer();
protected:
Resource<MeshView> bottom, cylinder, top;
};
template<> class AbstractCapsuleRenderer<3>: public AbstractShapeRenderer<3> {
public:
explicit AbstractCapsuleRenderer();
protected:
Resource<MeshView> bottom, cylinder, top;
};
template<UnsignedInt dimensions> class CapsuleRenderer: public AbstractCapsuleRenderer<dimensions> {
public:
explicit CapsuleRenderer(const Shapes::Implementation::AbstractShape<dimensions>& capsule);
CapsuleRenderer(const Shapes::Implementation::AbstractShape<dimensions>&&) = delete;
void draw(Resource<ShapeRendererOptions>& options, const typename DimensionTraits<dimensions, Float>::MatrixType& projectionMatrix) override;
private:
const Shapes::Capsule<dimensions>& capsule;
};
}}}
#endif

106
src/DebugTools/Implementation/CapsuleRendererTransformation.h

@ -0,0 +1,106 @@
#ifndef Magnum_DebugTools_Implementation_ForceRendererTransformation_h
#define Magnum_DebugTools_Implementation_ForceRendererTransformation_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <array>
#include "Math/Functions.h"
#include "Math/Matrix3.h"
#include "Math/Matrix4.h"
#include "Magnum.h"
#include "DimensionTraits.h"
namespace Magnum { namespace DebugTools { namespace Implementation {
template<UnsignedInt dimensions> std::array<typename DimensionTraits<dimensions, Float>::MatrixType, 3> capsuleRendererTransformation(const typename DimensionTraits<dimensions, Float>::VectorType& a, const typename DimensionTraits<dimensions, Float>::VectorType& b, Float radius);
template<> std::array<Matrix3, 3> capsuleRendererTransformation<2>(const Vector2& a, const Vector2& b, const Float radius) {
/* Vector from capsule center to top hemisphere center */
const Vector2 direction = 0.5f*(b - a);
const Float length = direction.length();
/* Capsule rotation and distance to caps after they are scaled to proper
radius (if nonzero cylinder length) */
Matrix3 rotation;
Vector2 capDistance;
if(length >= Math::TypeTraits<Float>::epsilon()) {
rotation.up() = direction/length;
rotation.right() = rotation.up().perpendicular();
CORRADE_INTERNAL_ASSERT(rotation.right().isNormalized());
capDistance = direction*(radius/length);
}
/* Scaling and translation of all parts */
const auto rotationScaling = rotation*Matrix3::scaling(Vector2(radius));
return {{
Matrix3::translation(a+capDistance)*rotationScaling,
Matrix3::translation(0.5f*(a + b))*rotation*Matrix3::scaling({radius, length}),
Matrix3::translation(b-capDistance)*rotationScaling
}};
}
template<> std::array<Matrix4, 3> capsuleRendererTransformation<3>(const Vector3& a, const Vector3& b, const Float radius) {
/* Vector from capsule center to top hemisphere center */
const Vector3 direction = 0.5f*(b - a);
const Float length = direction.length();
/* Capsule rotation and distance to caps after they are scaled to proper
radius (if nonzero cylinder length) */
Matrix4 rotation;
Vector3 capDistance;
if(length >= Math::TypeTraits<Float>::epsilon()) {
const Vector3 directionNormalized = direction/length;
const Float dot = Vector3::dot(directionNormalized, Vector3::zAxis());
/* Direction is parallel to Z axis, special rotation case */
if(Math::abs(dot) > 1.0f - Math::TypeTraits<Float>::epsilon()) {
rotation.up() = dot*Vector3::zAxis();
rotation.right() = Vector3::xAxis();
rotation.backward() = -dot*Vector3::yAxis();
/* Common case */
} else {
rotation.up() = directionNormalized;
rotation.right() = Vector3::cross(rotation.up(), Vector3::zAxis()).normalized();
rotation.backward() = Vector3::cross(rotation.right(), rotation.up());
CORRADE_INTERNAL_ASSERT(rotation.up().isNormalized() && rotation.backward().isNormalized());
}
capDistance = directionNormalized*radius;
}
/* Scaling and translation of all parts */
const auto rotationScaling = rotation*Matrix4::scaling(Vector3(radius));
return {{
Matrix4::translation(a+capDistance)*rotationScaling,
Matrix4::translation(0.5f*(a + b))*rotation*Matrix4::scaling({radius, length, radius}),
Matrix4::translation(b-capDistance)*rotationScaling
}};
}
}}}
#endif

61
src/DebugTools/Implementation/CylinderRenderer.cpp

@ -0,0 +1,61 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 "CylinderRenderer.h"
#include "Mesh.h"
#include "DebugTools/ShapeRenderer.h"
#include "Shapes/Cylinder.h"
#include "Primitives/Cylinder.h"
#include "Primitives/Square.h"
#include "Shaders/Flat.h"
#include "Trade/MeshData2D.h"
#include "Trade/MeshData3D.h"
#include "DebugTools/Implementation/CylinderRendererTransformation.h"
namespace Magnum { namespace DebugTools { namespace Implementation {
AbstractCylinderRenderer<2>::AbstractCylinderRenderer(): AbstractShapeRenderer<2>("cylinder2d", "cylinder2d-vertices", {}) {
if(!wireframeMesh) createResources(Primitives::Square::wireframe());
}
AbstractCylinderRenderer<3>::AbstractCylinderRenderer(): AbstractShapeRenderer<3>("cylinder3d", "cylinder3d-vertices", "cylinder3d-indices") {
if(!wireframeMesh) createResources(Primitives::Cylinder::wireframe(1, 40, 1.0f));
}
template<UnsignedInt dimensions> CylinderRenderer<dimensions>::CylinderRenderer(const Shapes::Implementation::AbstractShape<dimensions>& cylinder): cylinder(static_cast<const Shapes::Implementation::Shape<Shapes::Cylinder<dimensions>>&>(cylinder).shape) {}
template<UnsignedInt dimensions> void CylinderRenderer<dimensions>::draw(Resource<ShapeRendererOptions>& options, const typename DimensionTraits<dimensions, Float>::MatrixType& projectionMatrix) {
AbstractShapeRenderer<dimensions>::wireframeShader->setTransformationProjectionMatrix(projectionMatrix*
Implementation::cylinderRendererTransformation<dimensions>(cylinder.a(), cylinder.b(), cylinder.radius()))
.setColor(options->color())
.use();
AbstractShapeRenderer<dimensions>::wireframeMesh->draw();
}
template class CylinderRenderer<2>;
template class CylinderRenderer<3>;
}}}

60
src/DebugTools/Implementation/CylinderRenderer.h

@ -0,0 +1,60 @@
#ifndef Magnum_DebugTools_Implementation_CylinderRenderer_h
#define Magnum_DebugTools_Implementation_CylinderRenderer_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 "AbstractShapeRenderer.h"
#include "Shapes/Shapes.h"
#include "corradeCompatibility.h"
namespace Magnum { namespace DebugTools { namespace Implementation {
template<UnsignedInt dimensions> class AbstractCylinderRenderer;
template<> class AbstractCylinderRenderer<2>: public AbstractShapeRenderer<2> {
public:
explicit AbstractCylinderRenderer();
};
template<> class AbstractCylinderRenderer<3>: public AbstractShapeRenderer<3> {
public:
explicit AbstractCylinderRenderer();
};
template<UnsignedInt dimensions> class CylinderRenderer: public AbstractCylinderRenderer<dimensions> {
public:
explicit CylinderRenderer(const Shapes::Implementation::AbstractShape<dimensions>& cylinder);
CylinderRenderer(const Shapes::Implementation::AbstractShape<dimensions>&&) = delete;
void draw(Resource<ShapeRendererOptions>& options, const typename DimensionTraits<dimensions, Float>::MatrixType& projectionMatrix) override;
private:
const Shapes::Cylinder<dimensions>& cylinder;
};
}}}
#endif

88
src/DebugTools/Implementation/CylinderRendererTransformation.h

@ -0,0 +1,88 @@
#ifndef Magnum_DebugTools_Implementation_ForceRendererTransformation_h
#define Magnum_DebugTools_Implementation_ForceRendererTransformation_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 "Math/Functions.h"
#include "Math/Matrix3.h"
#include "Math/Matrix4.h"
#include "Magnum.h"
#include "DimensionTraits.h"
namespace Magnum { namespace DebugTools { namespace Implementation {
template<UnsignedInt dimensions> typename DimensionTraits<dimensions, Float>::MatrixType cylinderRendererTransformation(const typename DimensionTraits<dimensions, Float>::VectorType& a, const typename DimensionTraits<dimensions, Float>::VectorType& b, Float radius);
template<> Matrix3 cylinderRendererTransformation<2>(const Vector2& a, const Vector2& b, const Float radius) {
/* Vector from cylinder center to top hemisphere center */
const Vector2 direction = 0.5f*(b - a);
const Float length = direction.length();
/* Capsule rotation and distance to caps after they are scaled to proper
radius (if nonzero cylinder length) */
Matrix3 rotation;
if(length >= Math::TypeTraits<Float>::epsilon()) {
rotation.up() = direction/length;
rotation.right() = rotation.up().perpendicular();
CORRADE_INTERNAL_ASSERT(rotation.right().isNormalized());
}
/* Scaling and translation */
return Matrix3::translation(0.5f*(a + b))*rotation*Matrix3::scaling({radius, length});
}
template<> Matrix4 cylinderRendererTransformation<3>(const Vector3& a, const Vector3& b, const Float radius) {
/* Vector from cylinder center to top hemisphere center */
const Vector3 direction = 0.5f*(b - a);
const Float length = direction.length();
/* Capsule rotation and distance to caps after they are scaled to proper
radius (if nonzero cylinder length) */
Matrix4 rotation;
if(length >= Math::TypeTraits<Float>::epsilon()) {
const Vector3 directionNormalized = direction/length;
const Float dot = Vector3::dot(directionNormalized, Vector3::zAxis());
/* Direction is parallel to Z axis, special rotation case */
if(Math::abs(dot) > 1.0f - Math::TypeTraits<Float>::epsilon()) {
rotation.up() = dot*Vector3::zAxis();
rotation.right() = Vector3::xAxis();
rotation.backward() = -dot*Vector3::yAxis();
/* Common case */
} else {
rotation.up() = directionNormalized;
rotation.right() = Vector3::cross(rotation.up(), Vector3::zAxis()).normalized();
rotation.backward() = Vector3::cross(rotation.right(), rotation.up());
CORRADE_INTERNAL_ASSERT(rotation.up().isNormalized() && rotation.backward().isNormalized());
}
}
/* Scaling and translation */
return Matrix4::translation(0.5f*(a + b))*rotation*Matrix4::scaling({radius, length, radius});
}
}}}
#endif

14
src/DebugTools/Implementation/ForceRendererTransformation.h

@ -24,6 +24,7 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include "Math/Functions.h"
#include "Math/Matrix3.h" #include "Math/Matrix3.h"
#include "Math/Matrix4.h" #include "Math/Matrix4.h"
#include "Magnum.h" #include "Magnum.h"
@ -34,7 +35,7 @@ namespace Magnum { namespace DebugTools { namespace Implementation {
template<UnsignedInt dimensions> typename DimensionTraits<dimensions, Float>::MatrixType forceRendererTransformation(const typename DimensionTraits<dimensions, Float>::VectorType& forcePosition, const typename DimensionTraits<dimensions, Float>::VectorType& force); template<UnsignedInt dimensions> typename DimensionTraits<dimensions, Float>::MatrixType forceRendererTransformation(const typename DimensionTraits<dimensions, Float>::VectorType& forcePosition, const typename DimensionTraits<dimensions, Float>::VectorType& force);
template<> inline Matrix3 forceRendererTransformation<2>(const Vector2& forcePosition, const Vector2& force) { template<> inline Matrix3 forceRendererTransformation<2>(const Vector2& forcePosition, const Vector2& force) {
return Matrix3::from({force, Vector2(-force.y(), force.x())}, forcePosition); return Matrix3::from({force, force.perpendicular()}, forcePosition);
} }
template<> Matrix4 forceRendererTransformation<3>(const Vector3& forcePosition, const Vector3& force) { template<> Matrix4 forceRendererTransformation<3>(const Vector3& forcePosition, const Vector3& force) {
@ -48,18 +49,15 @@ template<> Matrix4 forceRendererTransformation<3>(const Vector3& forcePosition,
const Float dot = Vector3::dot(force/forceLength, Vector3::xAxis()); const Float dot = Vector3::dot(force/forceLength, Vector3::xAxis());
/* Force is parallel to X axis, just scaling */ /* Force is parallel to X axis, just scaling */
if(dot > 1.0f - Math::TypeTraits<Float>::epsilon()) if(Math::abs(dot) > 1.0f - Math::TypeTraits<Float>::epsilon())
return translation*Matrix4::scaling(Vector3(forceLength)); return translation*Matrix4::scaling({Math::sign(dot)*forceLength, forceLength, forceLength});
/* Force is antiparallel to X axis, scaling inverted on X */
if(-dot > 1.0f - Math::TypeTraits<Float>::epsilon())
return translation*Matrix4::scaling({-forceLength, forceLength, forceLength});
/* Normal of plane going through force vector and X axis vector */ /* Normal of plane going through force vector and X axis vector */
const Vector3 normal = Vector3::cross(Vector3::xAxis(), force).normalized(); const Vector3 normal = Vector3::cross(Vector3::xAxis(), force).normalized();
/* Third base vector, orthogonal to force and normal */ /* Third base vector, orthogonal to force and normal */
const Vector3 binormal = Vector3::cross(normal, force).normalized(); const Vector3 binormal = Vector3::cross(normal, force/forceLength);
CORRADE_INTERNAL_ASSERT(binormal.isNormalized());
/* Transformation matrix from scaled base vectors and translation vector */ /* Transformation matrix from scaled base vectors and translation vector */
return Matrix4::from({force, normal*forceLength, binormal*forceLength}, forcePosition); return Matrix4::from({force, normal*forceLength, binormal*forceLength}, forcePosition);

2
src/DebugTools/Implementation/SphereRenderer.cpp

@ -40,7 +40,7 @@ AbstractSphereRenderer<2>::AbstractSphereRenderer(): AbstractShapeRenderer<2>("s
} }
AbstractSphereRenderer<3>::AbstractSphereRenderer(): AbstractShapeRenderer<3>("sphere3d", "sphere3d-vertices", "sphere3d-indices") { AbstractSphereRenderer<3>::AbstractSphereRenderer(): AbstractShapeRenderer<3>("sphere3d", "sphere3d-vertices", "sphere3d-indices") {
if(!wireframeMesh) createResources(Primitives::UVSphere::wireframe(40, 20)); if(!wireframeMesh) createResources(Primitives::UVSphere::wireframe(20, 40));
} }
template<UnsignedInt dimensions> SphereRenderer<dimensions>::SphereRenderer(const Shapes::Implementation::AbstractShape<dimensions>& sphere): sphere(static_cast<const Shapes::Implementation::Shape<Shapes::Sphere<dimensions>>&>(sphere).shape) {} template<UnsignedInt dimensions> SphereRenderer<dimensions>::SphereRenderer(const Shapes::Implementation::AbstractShape<dimensions>& sphere): sphere(static_cast<const Shapes::Implementation::Shape<Shapes::Sphere<dimensions>>&>(sphere).shape) {}

3
src/DebugTools/ResourceManager.cpp

@ -28,13 +28,14 @@
#include "Buffer.h" #include "Buffer.h"
#include "Mesh.h" #include "Mesh.h"
#include "MeshView.h"
#include "DebugTools/ForceRenderer.h" #include "DebugTools/ForceRenderer.h"
#include "DebugTools/ObjectRenderer.h" #include "DebugTools/ObjectRenderer.h"
#include "DebugTools/ShapeRenderer.h" #include "DebugTools/ShapeRenderer.h"
namespace Magnum { namespace Magnum {
template class ResourceManager<AbstractShaderProgram, Buffer, Mesh, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions>; template class ResourceManager<AbstractShaderProgram, Buffer, Mesh, MeshView, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions>;
namespace DebugTools { namespace DebugTools {

6
src/DebugTools/ResourceManager.h

@ -43,7 +43,9 @@
namespace Magnum { namespace Magnum {
extern template ResourceManager<AbstractShaderProgram, Buffer, Mesh, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions> MAGNUM_DEBUGTOOLS_EXPORT *& ResourceManager<AbstractShaderProgram, Buffer, Mesh, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions>::internalInstance(); /** @todo Do the listing in one place, not five thousand! */
extern template ResourceManager<AbstractShaderProgram, Buffer, Mesh, MeshView, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions> MAGNUM_DEBUGTOOLS_EXPORT *& ResourceManager<AbstractShaderProgram, Buffer, Mesh, MeshView, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions>::internalInstance();
namespace DebugTools { namespace DebugTools {
@ -53,7 +55,7 @@ namespace DebugTools {
Stores various data used by debug renderers. See @ref debug-tools for more Stores various data used by debug renderers. See @ref debug-tools for more
information. information.
*/ */
class MAGNUM_DEBUGTOOLS_EXPORT ResourceManager: public Magnum::ResourceManager<AbstractShaderProgram, Buffer, Mesh, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions> { class MAGNUM_DEBUGTOOLS_EXPORT ResourceManager: public Magnum::ResourceManager<AbstractShaderProgram, Buffer, Mesh, MeshView, DebugTools::ForceRendererOptions, DebugTools::ObjectRendererOptions, DebugTools::ShapeRendererOptions> {
public: public:
explicit ResourceManager(); explicit ResourceManager();
~ResourceManager(); ~ResourceManager();

14
src/DebugTools/ShapeRenderer.cpp

@ -31,6 +31,8 @@
#include "Implementation/AxisAlignedBoxRenderer.h" #include "Implementation/AxisAlignedBoxRenderer.h"
#include "Implementation/BoxRenderer.h" #include "Implementation/BoxRenderer.h"
#include "Implementation/CapsuleRenderer.h"
#include "Implementation/CylinderRenderer.h"
#include "Implementation/LineSegmentRenderer.h" #include "Implementation/LineSegmentRenderer.h"
#include "Implementation/PointRenderer.h" #include "Implementation/PointRenderer.h"
#include "Implementation/SphereRenderer.h" #include "Implementation/SphereRenderer.h"
@ -56,6 +58,12 @@ template<> void createDebugMesh(ShapeRenderer<2>& renderer, const Shapes::Implem
case Shapes::AbstractShape2D::Type::Sphere: case Shapes::AbstractShape2D::Type::Sphere:
renderer.renderers.push_back(new Implementation::SphereRenderer<2>(shape)); renderer.renderers.push_back(new Implementation::SphereRenderer<2>(shape));
break; break;
case Shapes::AbstractShape2D::Type::Capsule:
renderer.renderers.push_back(new Implementation::CapsuleRenderer<2>(shape));
break;
case Shapes::AbstractShape2D::Type::Cylinder:
renderer.renderers.push_back(new Implementation::CylinderRenderer<2>(shape));
break;
case Shapes::AbstractShape2D::Type::Composition: { case Shapes::AbstractShape2D::Type::Composition: {
const Shapes::Composition2D& composition = const Shapes::Composition2D& composition =
static_cast<const Shapes::Implementation::Shape<Shapes::Composition2D>&>(shape).shape; static_cast<const Shapes::Implementation::Shape<Shapes::Composition2D>&>(shape).shape;
@ -84,6 +92,12 @@ template<> void createDebugMesh(ShapeRenderer<3>& renderer, const Shapes::Implem
case Shapes::AbstractShape3D::Type::Sphere: case Shapes::AbstractShape3D::Type::Sphere:
renderer.renderers.push_back(new Implementation::SphereRenderer<3>(shape)); renderer.renderers.push_back(new Implementation::SphereRenderer<3>(shape));
break; break;
case Shapes::AbstractShape3D::Type::Capsule:
renderer.renderers.push_back(new Implementation::CapsuleRenderer<3>(shape));
break;
case Shapes::AbstractShape3D::Type::Cylinder:
renderer.renderers.push_back(new Implementation::CylinderRenderer<3>(shape));
break;
case Shapes::AbstractShape3D::Type::Composition: { case Shapes::AbstractShape3D::Type::Composition: {
const Shapes::Composition3D& composition = const Shapes::Composition3D& composition =
static_cast<const Shapes::Implementation::Shape<Shapes::Composition3D>&>(shape).shape; static_cast<const Shapes::Implementation::Shape<Shapes::Composition3D>&>(shape).shape;

2
src/DebugTools/Test/CMakeLists.txt

@ -22,5 +22,7 @@
# DEALINGS IN THE SOFTWARE. # DEALINGS IN THE SOFTWARE.
# #
corrade_add_test(DebugToolsCapsuleRendererTest CapsuleRendererTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(DebugToolsCylinderRendererTest CylinderRendererTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(DebugToolsForceRendererTest ForceRendererTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(DebugToolsForceRendererTest ForceRendererTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(DebugToolsLineSegmentRendererTest LineSegmentRendererTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(DebugToolsLineSegmentRendererTest LineSegmentRendererTest.cpp LIBRARIES MagnumMathTestLib)

177
src/DebugTools/Test/CapsuleRendererTest.cpp

@ -0,0 +1,177 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <TestSuite/Tester.h>
#include "DebugTools/Implementation/CapsuleRendererTransformation.h"
namespace Magnum { namespace DebugTools { namespace Test {
class CapsuleRendererTest: public TestSuite::Tester {
public:
explicit CapsuleRendererTest();
void zeroLength2D();
void common2D();
void zeroLength3D();
void parallel3D();
void antiParallel3D();
void common3D();
};
CapsuleRendererTest::CapsuleRendererTest() {
addTests({&CapsuleRendererTest::zeroLength2D,
&CapsuleRendererTest::common2D,
&CapsuleRendererTest::zeroLength3D,
&CapsuleRendererTest::parallel3D,
&CapsuleRendererTest::antiParallel3D,
&CapsuleRendererTest::common3D});
}
void CapsuleRendererTest::zeroLength2D() {
const Vector2 a(0.5f, 3.0f);
std::array<Matrix3, 3> transformation = Implementation::capsuleRendererTransformation<2>(a, a, 3.5f);
const auto scaling = Math::Matrix<2, Float>::fromDiagonal(Vector2(3.5f));
CORRADE_COMPARE(transformation[0].rotationScaling(), scaling);
CORRADE_COMPARE(transformation[1].rotationScaling(), (Math::Matrix<2, Float>::fromDiagonal({3.5f, 0.0f})));
CORRADE_COMPARE(transformation[2].rotationScaling(), scaling);
CORRADE_COMPARE(transformation[0].translation(), a);
CORRADE_COMPARE(transformation[1].translation(), a);
CORRADE_COMPARE(transformation[2].translation(), a);
}
void CapsuleRendererTest::common2D() {
const Vector2 a(0.5f, 3.0f);
const Vector2 b(7.5f, -1.0f);
std::array<Matrix3, 3> transformation = Implementation::capsuleRendererTransformation<2>(a, b, 3.5f);
/* Vector from capsule center to top hemisphere center */
const Vector2 up(3.5f, -2.0f);
CORRADE_COMPARE(transformation[0].up(), up.resized(3.5f));
CORRADE_COMPARE(transformation[1].up(), up);
CORRADE_COMPARE(transformation[2].up(), up.resized(3.5f));
const auto right = Vector2(4.0f, 7.0f).resized(3.5f);
CORRADE_COMPARE(transformation[0].right(), right);
CORRADE_COMPARE(transformation[1].right(), right);
CORRADE_COMPARE(transformation[2].right(), right);
/* Orthogonality */
CORRADE_COMPARE(Vector2::dot(transformation[0].up(), transformation[0].right()), 0.0f);
const Vector2 capDistance = up.resized(3.5f);
CORRADE_COMPARE(transformation[0].translation(), a+capDistance);
CORRADE_COMPARE(transformation[1].translation(), 0.5f*(a + b));
CORRADE_COMPARE(transformation[2].translation(), b-capDistance);
}
void CapsuleRendererTest::zeroLength3D() {
const Vector3 a(0.5f, 3.0f, 7.0f);
std::array<Matrix4, 3> transformation = Implementation::capsuleRendererTransformation<3>(a, a, 3.5f);
const auto scaling = Math::Matrix<3, Float>::fromDiagonal(Vector3(3.5f));
CORRADE_COMPARE(transformation[0].rotationScaling(), scaling);
CORRADE_COMPARE(transformation[1].rotationScaling(), (Math::Matrix<3, Float>::fromDiagonal({3.5f, 0.0f, 3.5f})));
CORRADE_COMPARE(transformation[2].rotationScaling(), scaling);
CORRADE_COMPARE(transformation[0].translation(), a);
CORRADE_COMPARE(transformation[1].translation(), a);
CORRADE_COMPARE(transformation[2].translation(), a);
}
void CapsuleRendererTest::parallel3D() {
const Vector3 a(0.5f, 3.0f, 7.0f);
const Vector3 b(0.5f, 3.0f, 11.0f);
std::array<Matrix4, 3> transformation = Implementation::capsuleRendererTransformation<3>(a, b, 3.5f);
const auto rotation = Matrix4::rotationX(Deg(90.0f));
const auto scaling = (rotation*Matrix4::scaling(Vector3(3.5f))).rotationScaling();
CORRADE_COMPARE(transformation[0].rotationScaling(), scaling);
CORRADE_COMPARE(transformation[1].rotationScaling(),
(rotation*Matrix4::scaling({3.5f, 2.0f, 3.5f})).rotationScaling());
CORRADE_COMPARE(transformation[2].rotationScaling(), scaling);
const auto capDistance = Vector3::zAxis(3.5f);
CORRADE_COMPARE(transformation[0].translation(), a+capDistance);
CORRADE_COMPARE(transformation[1].translation(), a+Vector3::zAxis(2.0f));
CORRADE_COMPARE(transformation[2].translation(), b-capDistance);
}
void CapsuleRendererTest::antiParallel3D() {
const Vector3 a(0.5f, 3.0f, 7.0f);
const Vector3 b(0.5f, 3.0f, 3.0f);
std::array<Matrix4, 3> transformation = Implementation::capsuleRendererTransformation<3>(a, b, 3.5f);
const auto rotation = Matrix4::rotationX(-Deg(90.0f));
const auto rotationScaling = (rotation*Matrix4::scaling(Vector3(3.5f))).rotationScaling();
CORRADE_COMPARE(transformation[0].rotationScaling(), rotationScaling);
CORRADE_COMPARE(transformation[1].rotationScaling(),
(rotation*Matrix4::scaling({3.5f, 2.0f, 3.5f})).rotationScaling());
CORRADE_COMPARE(transformation[2].rotationScaling(), rotationScaling);
const auto capDistance = Vector3::zAxis(-3.5f);
CORRADE_COMPARE(transformation[0].translation(), a+capDistance);
CORRADE_COMPARE(transformation[1].translation(), a+Vector3::zAxis(-2.0f));
CORRADE_COMPARE(transformation[2].translation(), b-capDistance);
}
void CapsuleRendererTest::common3D() {
const Vector3 a(0.5f, 3.0f, 7.0f);
const Vector3 b(7.5f, -1.0f, 1.5f);
std::array<Matrix4, 3> transformation = Implementation::capsuleRendererTransformation<3>(a, b, 3.5f);
/* Vector from capsule center to top hemisphere center */
const Vector3 up(3.5f, -2.0f, -2.75f);
CORRADE_COMPARE(transformation[0].up(), up.resized(3.5f));
CORRADE_COMPARE(transformation[1].up(), up);
CORRADE_COMPARE(transformation[2].up(), up.resized(3.5f));
const auto right = Vector3(-2.0f, -3.5f, 0.0f).resized(3.5f);
CORRADE_COMPARE(transformation[0].right(), right);
CORRADE_COMPARE(transformation[1].right(), right);
CORRADE_COMPARE(transformation[2].right(), right);
const auto backward = Vector3(9.625f, -5.5f, 16.25f).resized(3.5f);
CORRADE_COMPARE(transformation[0].backward(), backward);
CORRADE_COMPARE(transformation[1].backward(), backward);
CORRADE_COMPARE(transformation[2].backward(), backward);
/* Orthogonality */
CORRADE_COMPARE(Vector3::dot(transformation[0].up(), transformation[0].right()), 0.0f);
CORRADE_COMPARE(Vector3::dot(transformation[0].up(), transformation[0].backward()), 0.0f);
CORRADE_COMPARE(Vector3::dot(transformation[0].right(), transformation[0].backward()), 0.0f);
const Vector3 capDistance = up.resized(3.5f);
CORRADE_COMPARE(transformation[0].translation(), a+capDistance);
CORRADE_COMPARE(transformation[1].translation(), 0.5f*(a + b));
CORRADE_COMPARE(transformation[2].translation(), b-capDistance);
}
}}}
CORRADE_TEST_MAIN(Magnum::DebugTools::Test::CapsuleRendererTest)

125
src/DebugTools/Test/CylinderRendererTest.cpp

@ -0,0 +1,125 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <TestSuite/Tester.h>
#include "DebugTools/Implementation/CylinderRendererTransformation.h"
namespace Magnum { namespace DebugTools { namespace Test {
class CylinderRendererTest: public TestSuite::Tester {
public:
explicit CylinderRendererTest();
void zeroLength2D();
void common2D();
void zeroLength3D();
void parallel3D();
void antiParallel3D();
void common3D();
};
CylinderRendererTest::CylinderRendererTest() {
addTests({&CylinderRendererTest::zeroLength2D,
&CylinderRendererTest::common2D,
&CylinderRendererTest::zeroLength3D,
&CylinderRendererTest::parallel3D,
&CylinderRendererTest::antiParallel3D,
&CylinderRendererTest::common3D});
}
void CylinderRendererTest::zeroLength2D() {
const Vector2 a(0.5f, 3.0f);
const Matrix3 transformation = Implementation::cylinderRendererTransformation<2>(a, a, 3.5f);
CORRADE_COMPARE(transformation.rotationScaling(), (Math::Matrix<2, Float>::fromDiagonal({3.5f, 0.0f})));
CORRADE_COMPARE(transformation.translation(), a);
}
void CylinderRendererTest::common2D() {
const Vector2 a(0.5f, 3.0f);
const Vector2 b(7.5f, -1.0f);
const Matrix3 transformation = Implementation::cylinderRendererTransformation<2>(a, b, 3.5f);
/* Rotation + scaling, test orthogonality */
CORRADE_COMPARE(transformation.up(), Vector2(3.5f, -2.0f));
CORRADE_COMPARE(transformation.right(), Vector2(4.0f, 7.0f).resized(3.5f));
CORRADE_COMPARE(Vector2::dot(transformation.up(), transformation.right()), 0.0f);
CORRADE_COMPARE(transformation.translation(), 0.5f*(a + b));
}
void CylinderRendererTest::zeroLength3D() {
const Vector3 a(0.5f, 3.0f, 7.0f);
const Matrix4 transformation = Implementation::cylinderRendererTransformation<3>(a, a, 3.5f);
CORRADE_COMPARE(transformation.rotationScaling(), (Math::Matrix<3, Float>::fromDiagonal({3.5f, 0.0f, 3.5f})));
CORRADE_COMPARE(transformation.translation(), a);
}
void CylinderRendererTest::parallel3D() {
const Vector3 a(0.5f, 3.0f, 7.0f);
const Vector3 b(0.5f, 3.0f, 11.0f);
const Matrix4 transformation = Implementation::cylinderRendererTransformation<3>(a, b, 3.5f);
CORRADE_COMPARE(transformation.rotationScaling(),
(Matrix4::rotationX(Deg(90.0f))*Matrix4::scaling({3.5f, 2.0f, 3.5f})).rotationScaling());
CORRADE_COMPARE(transformation.translation(), a+Vector3::zAxis(2.0f));
}
void CylinderRendererTest::antiParallel3D() {
const Vector3 a(0.5f, 3.0f, 7.0f);
const Vector3 b(0.5f, 3.0f, 3.0f);
const Matrix4 transformation = Implementation::cylinderRendererTransformation<3>(a, b, 3.5f);
CORRADE_COMPARE(transformation.rotationScaling(),
(Matrix4::rotationX(-Deg(90.0f))*Matrix4::scaling({3.5f, 2.0f, 3.5f})).rotationScaling());
CORRADE_COMPARE(transformation.translation(), a+Vector3::zAxis(-2.0f));
}
void CylinderRendererTest::common3D() {
const Vector3 a(0.5f, 3.0f, 7.0f);
const Vector3 b(7.5f, -1.0f, 1.5f);
const Matrix4 transformation = Implementation::cylinderRendererTransformation<3>(a, b, 3.5f);
/* Rotation + scaling */
CORRADE_COMPARE(transformation.up(), Vector3(3.5f, -2.0f, -2.75f));
CORRADE_COMPARE(transformation.right(), Vector3(-2.0f, -3.5f, 0.0f).resized(3.5f));
CORRADE_COMPARE(transformation.backward(), Vector3(9.625f, -5.5f, 16.25f).resized(3.5f));
/* Orthogonality */
CORRADE_COMPARE(Vector3::dot(transformation.up(), transformation.right()), 0.0f);
CORRADE_COMPARE(Vector3::dot(transformation.up(), transformation.backward()), 0.0f);
CORRADE_COMPARE(Vector3::dot(transformation.right(), transformation.backward()), 0.0f);
CORRADE_COMPARE(transformation.translation(), 0.5f*(a + b));
}
}}}
CORRADE_TEST_MAIN(Magnum::DebugTools::Test::CylinderRendererTest)

27
src/DebugTools/Test/ForceRendererTest.cpp

@ -33,9 +33,7 @@ class ForceRendererTest: public TestSuite::Tester {
explicit ForceRendererTest(); explicit ForceRendererTest();
void zero2D(); void zero2D();
void parallel2D(); void common2D();
void antiParallel2D();
void arbitrary2D();
void zero3D(); void zero3D();
void parallel3D(); void parallel3D();
@ -45,9 +43,7 @@ class ForceRendererTest: public TestSuite::Tester {
ForceRendererTest::ForceRendererTest() { ForceRendererTest::ForceRendererTest() {
addTests({&ForceRendererTest::zero2D, addTests({&ForceRendererTest::zero2D,
&ForceRendererTest::parallel2D, &ForceRendererTest::common2D,
&ForceRendererTest::antiParallel2D,
&ForceRendererTest::arbitrary2D,
&ForceRendererTest::zero3D, &ForceRendererTest::zero3D,
&ForceRendererTest::parallel3D, &ForceRendererTest::parallel3D,
@ -60,17 +56,7 @@ void ForceRendererTest::zero2D() {
Matrix3::translation({0.5f, -3.0f})*Matrix3::scaling(Vector2(0.0f))); Matrix3::translation({0.5f, -3.0f})*Matrix3::scaling(Vector2(0.0f)));
} }
void ForceRendererTest::parallel2D() { void ForceRendererTest::common2D() {
CORRADE_COMPARE(Implementation::forceRendererTransformation<2>({0.5f, -3.0f}, Vector2::xAxis(2.5f)),
Matrix3::translation({0.5f, -3.0f})*Matrix3::scaling(Vector2(2.5f)));
}
void ForceRendererTest::antiParallel2D() {
CORRADE_COMPARE(Implementation::forceRendererTransformation<2>({0.5f, -3.0f}, Vector2::xAxis(-2.5f)),
Matrix3::translation({0.5f, -3.0f})*Matrix3::scaling(Vector2(-2.5f)));
}
void ForceRendererTest::arbitrary2D() {
Vector2 force(2.7f, -11.5f); Vector2 force(2.7f, -11.5f);
Matrix3 m = Implementation::forceRendererTransformation<2>({0.5f, -3.0f}, force); Matrix3 m = Implementation::forceRendererTransformation<2>({0.5f, -3.0f}, force);
@ -81,7 +67,7 @@ void ForceRendererTest::arbitrary2D() {
/* All vectors have the same length */ /* All vectors have the same length */
CORRADE_COMPARE(m.up().length(), force.length()); CORRADE_COMPARE(m.up().length(), force.length());
/* All vectors are parallel */ /* All vectors are orthogonal */
CORRADE_COMPARE(Vector2::dot(m.right(), m.up()), 0.0f); CORRADE_COMPARE(Vector2::dot(m.right(), m.up()), 0.0f);
} }
@ -112,10 +98,11 @@ void ForceRendererTest::arbitrary3D() {
CORRADE_COMPARE(m.up().length(), force.length()); CORRADE_COMPARE(m.up().length(), force.length());
CORRADE_COMPARE(m.backward().length(), force.length()); CORRADE_COMPARE(m.backward().length(), force.length());
/* All vectors are parallel */ /* All vectors are orthogonal */
CORRADE_COMPARE(Vector3::dot(m.right(), m.up()), 0.0f); CORRADE_COMPARE(Vector3::dot(m.right(), m.up()), 0.0f);
CORRADE_COMPARE(Vector3::dot(m.right(), m.backward()), 0.0f); CORRADE_COMPARE(Vector3::dot(m.right(), m.backward()), 0.0f);
CORRADE_COMPARE(Vector3::dot(m.up(), m.backward()), 0.0f); /** @todo This shouldn't be too different */
CORRADE_COMPARE(Vector3::dot(m.up(), m.backward()), -Math::TypeTraits<Float>::epsilon());
} }
}}}} }}}}

2
src/Framebuffer.h

@ -161,7 +161,7 @@ class MAGNUM_EXPORT Framebuffer: public AbstractFramebuffer {
* @see attachRenderbuffer(), attachTexture1D(), attachTexture2D(), * @see attachRenderbuffer(), attachTexture1D(), attachTexture2D(),
* attachCubeMapTexture(), attachTexture3D() * attachCubeMapTexture(), attachTexture3D()
*/ */
class BufferAttachment { class MAGNUM_EXPORT BufferAttachment {
public: public:
/** @brief Depth buffer */ /** @brief Depth buffer */
static const BufferAttachment Depth; static const BufferAttachment Depth;

1
src/Magnum.h

@ -392,6 +392,7 @@ typedef ImageReference<2> ImageReference2D;
typedef ImageReference<3> ImageReference3D; typedef ImageReference<3> ImageReference3D;
class Mesh; class Mesh;
class MeshView;
/* AbstractQuery is not used directly */ /* AbstractQuery is not used directly */
class PrimitiveQuery; class PrimitiveQuery;

45
src/Math/Matrix3.h

@ -168,7 +168,7 @@ template<class T> class Matrix3: public Matrix<3, T> {
* *
* Upper-left 2x2 part of the matrix. * Upper-left 2x2 part of the matrix.
* @see from(const Matrix<2, T>&, const Vector2&), rotation() const * @see from(const Matrix<2, T>&, const Vector2&), rotation() const
* rotationNormalized(), rotation(T), * rotationNormalized(), @ref uniformScaling(), rotation(T),
* Matrix4::rotationScaling() const * Matrix4::rotationScaling() const
*/ */
constexpr Matrix<2, T> rotationScaling() const { constexpr Matrix<2, T> rotationScaling() const {
@ -181,7 +181,8 @@ template<class T> class Matrix3: public Matrix<3, T> {
* *
* Similar to @ref rotationScaling(), but additionally checks that the * Similar to @ref rotationScaling(), but additionally checks that the
* base vectors are normalized. * base vectors are normalized.
* @see rotation() const, @ref Matrix4::rotationNormalized() * @see rotation() const, @ref uniformScaling(),
* @ref Matrix4::rotationNormalized()
* @todo assert also orthogonality or this is good enough? * @todo assert also orthogonality or this is good enough?
*/ */
Matrix<2, T> rotationNormalized() const { Matrix<2, T> rotationNormalized() const {
@ -194,17 +195,47 @@ template<class T> class Matrix3: public Matrix<3, T> {
/** /**
* @brief 2D rotation part of the matrix * @brief 2D rotation part of the matrix
* *
* Normalized upper-left 2x2 part of the matrix. * Normalized upper-left 2x2 part of the matrix. Expects uniform
* @see rotationNormalized(), rotationScaling() const, rotation(T), * scaling.
* Matrix4::rotation() const * @see rotationNormalized(), rotationScaling(), @ref uniformScaling(),
* @todo assert uniform scaling (otherwise this would be garbage) * rotation(T), Matrix4::rotation() const
*/ */
Matrix<2, T> rotation() const { Matrix<2, T> rotation() const {
CORRADE_ASSERT(TypeTraits<T>::equals((*this)[0].xy().dot(), (*this)[1].xy().dot()),
"Math::Matrix3::rotation(): the matrix doesn't have uniform scaling", {});
return {(*this)[0].xy().normalized(), return {(*this)[0].xy().normalized(),
(*this)[1].xy().normalized()}; (*this)[1].xy().normalized()};
} }
/** @todo uniform scaling extraction */ /**
* @brief Uniform scaling part of the matrix, squared
*
* Squared length of vectors in upper-left 2x2 part of the matrix.
* Expects that the scaling is the same in all axes. Faster alternative
* to @ref uniformScaling(), because it doesn't compute the square
* root.
* @see @ref rotationScaling(), @ref rotation(),
* @ref rotationNormalized(), @ref scaling(const Vector2&),
* @ref Matrix4::uniformScaling()
*/
T uniformScalingSquared() const {
const T scalingSquared = (*this)[0].xy().dot();
CORRADE_ASSERT(TypeTraits<T>::equals((*this)[1].xy().dot(), scalingSquared),
"Math::Matrix3::uniformScaling(): the matrix doesn't have uniform scaling", {});
return scalingSquared;
}
/**
* @brief Uniform scaling part of the matrix
*
* Length of vectors in upper-left 2x2 part of the matrix. Expects that
* the scaling is the same in all axes. Use faster alternative
* @ref uniformScalingSquared() where possible.
* @see @ref rotationScaling(), @ref rotation(),
* @ref rotationNormalized(), @ref scaling(const Vector2&),
* @ref Matrix4::uniformScaling()
*/
T uniformScaling() const { return std::sqrt(uniformScalingSquared()); }
/** /**
* @brief Right-pointing 2D vector * @brief Right-pointing 2D vector

50
src/Math/Matrix4.h

@ -231,8 +231,8 @@ template<class T> class Matrix4: public Matrix<4, T> {
* *
* Upper-left 3x3 part of the matrix. * Upper-left 3x3 part of the matrix.
* @see from(const Matrix<3, T>&, const Vector3&), rotation() const, * @see from(const Matrix<3, T>&, const Vector3&), rotation() const,
* rotationNormalized(), rotation(T, const Vector3&), * rotationNormalized(), @ref uniformScaling(),
* Matrix3::rotationScaling() const * rotation(T, const Vector3&), Matrix3::rotationScaling() const
*/ */
/* Not Matrix3, because it is for affine 2D transformations */ /* Not Matrix3, because it is for affine 2D transformations */
constexpr Matrix<3, T> rotationScaling() const { constexpr Matrix<3, T> rotationScaling() const {
@ -246,7 +246,8 @@ template<class T> class Matrix4: public Matrix<4, T> {
* *
* Similar to @ref rotationScaling(), but additionally checks that the * Similar to @ref rotationScaling(), but additionally checks that the
* base vectors are normalized. * base vectors are normalized.
* @see rotation() const, @ref Matrix3::rotationNormalized() * @see rotation() const, @ref uniformScaling(),
* @ref Matrix3::rotationNormalized()
* @todo assert also orthogonality or this is good enough? * @todo assert also orthogonality or this is good enough?
*/ */
/* Not Matrix3, because it is for affine 2D transformations */ /* Not Matrix3, because it is for affine 2D transformations */
@ -261,15 +262,39 @@ template<class T> class Matrix4: public Matrix<4, T> {
/** /**
* @brief 3D rotation part of the matrix * @brief 3D rotation part of the matrix
* *
* Normalized upper-left 3x3 part of the matrix. * Normalized upper-left 3x3 part of the matrix. Expects uniform
* scaling.
* @see rotationNormalized(), rotationScaling() const, * @see rotationNormalized(), rotationScaling() const,
* rotation(T, const Vector3&), Matrix3::rotation() const * @ref uniformScaling(), rotation(T, const Vector3&),
* @todo assert uniform scaling (otherwise this would be garbage) * Matrix3::rotation() const
*/ */
/* Not Matrix3, because it is for affine 2D transformations */ /* Not Matrix3, because it is for affine 2D transformations */
Matrix<3, T> rotation() const; Matrix<3, T> rotation() const;
/** @todo uniform scaling extraction */ /**
* @brief Uniform scaling part of the matrix, squared
*
* Squared length of vectors in upper-left 3x3 part of the matrix.
* Expects that the scaling is the same in all axes. Faster alternative
* to @ref uniformScaling(), because it doesn't compute the square
* root.
* @see @ref rotationScaling(), @ref rotation(),
* @ref rotationNormalized(), @ref scaling(const Vector3&),
* @ref Matrix3::uniformScaling()
*/
T uniformScalingSquared() const;
/**
* @brief Uniform scaling part of the matrix
*
* Length of vectors in upper-left 3x3 part of the matrix. Expects that
* the scaling is the same in all axes. Use faster alternative
* @ref uniformScalingSquared() where possible.
* @see @ref rotationScaling(), @ref rotation(),
* @ref rotationNormalized(), @ref scaling(const Vector3&),
* @ref Matrix3::uniformScaling()
*/
T uniformScaling() const { return std::sqrt(uniformScalingSquared()); }
/** /**
* @brief Right-pointing 3D vector * @brief Right-pointing 3D vector
@ -457,11 +482,22 @@ template<class T> Matrix4<T> Matrix4<T>::perspectiveProjection(const Vector2<T>&
} }
template<class T> inline Matrix<3, T> Matrix4<T>::rotation() const { template<class T> inline Matrix<3, T> Matrix4<T>::rotation() const {
CORRADE_ASSERT(TypeTraits<T>::equals((*this)[0].xyz().dot(), (*this)[1].xyz().dot()) &&
TypeTraits<T>::equals((*this)[1].xyz().dot(), (*this)[2].xyz().dot()),
"Math::Matrix4::rotation(): the matrix doesn't have uniform scaling", {});
return {(*this)[0].xyz().normalized(), return {(*this)[0].xyz().normalized(),
(*this)[1].xyz().normalized(), (*this)[1].xyz().normalized(),
(*this)[2].xyz().normalized()}; (*this)[2].xyz().normalized()};
} }
template<class T> T Matrix4<T>::uniformScalingSquared() const {
const T scalingSquared = (*this)[0].xyz().dot();
CORRADE_ASSERT(TypeTraits<T>::equals((*this)[1].xyz().dot(), scalingSquared) &&
TypeTraits<T>::equals((*this)[2].xyz().dot(), scalingSquared),
"Math::Matrix4::uniformScaling(): the matrix doesn't have uniform scaling", {});
return scalingSquared;
}
template<class T> Matrix4<T> Matrix4<T>::invertedRigid() const { template<class T> Matrix4<T> Matrix4<T>::invertedRigid() const {
CORRADE_ASSERT(isRigidTransformation(), CORRADE_ASSERT(isRigidTransformation(),
"Math::Matrix4::invertedRigid(): the matrix doesn't represent rigid transformation", {}); "Math::Matrix4::invertedRigid(): the matrix doesn't represent rigid transformation", {});

31
src/Math/Test/Matrix3Test.cpp

@ -78,6 +78,7 @@ class Matrix3Test: public Corrade::TestSuite::Tester {
void rotationScalingPart(); void rotationScalingPart();
void rotationNormalizedPart(); void rotationNormalizedPart();
void rotationPart(); void rotationPart();
void uniformScalingPart();
void vectorParts(); void vectorParts();
void invertedRigid(); void invertedRigid();
void transform(); void transform();
@ -113,6 +114,7 @@ Matrix3Test::Matrix3Test() {
&Matrix3Test::rotationScalingPart, &Matrix3Test::rotationScalingPart,
&Matrix3Test::rotationNormalizedPart, &Matrix3Test::rotationNormalizedPart,
&Matrix3Test::rotationPart, &Matrix3Test::rotationPart,
&Matrix3Test::uniformScalingPart,
&Matrix3Test::vectorParts, &Matrix3Test::vectorParts,
&Matrix3Test::invertedRigid, &Matrix3Test::invertedRigid,
&Matrix3Test::transform, &Matrix3Test::transform,
@ -322,21 +324,32 @@ void Matrix3Test::rotationPart() {
CORRADE_COMPARE(rotationTranslationPart, expectedRotationPart); CORRADE_COMPARE(rotationTranslationPart, expectedRotationPart);
/* Test uniform scaling */ /* Test uniform scaling */
Matrix3 rotationScaling = rotation*Matrix3::scaling(Vector2(9.0f)); Matrix3 rotationScaling = rotation*Matrix3::scaling(Vector2(3.0f));
Matrix2 rotationScalingPart = rotationScaling.rotation(); Matrix2 rotationScalingPart = rotationScaling.rotation();
CORRADE_COMPARE(rotationScalingPart.determinant(), 1.0f); CORRADE_COMPARE(rotationScalingPart.determinant(), 1.0f);
CORRADE_COMPARE(rotationScalingPart*rotationScalingPart.transposed(), Matrix2()); CORRADE_COMPARE(rotationScalingPart*rotationScalingPart.transposed(), Matrix2());
CORRADE_COMPARE(rotationScalingPart, expectedRotationPart); CORRADE_COMPARE(rotationScalingPart, expectedRotationPart);
/* Fails on non-uniform scaling */ /* Fails on non-uniform scaling */
{ std::ostringstream o;
CORRADE_EXPECT_FAIL("Assertion on uniform scaling is not implemented yet."); Error::setOutput(&o);
std::ostringstream o; Matrix2 rotationScaling2 = (rotation*Matrix3::scaling(Vector2::yScale(3.5f))).rotation();
Error::setOutput(&o); CORRADE_COMPARE(o.str(), "Math::Matrix3::rotation(): the matrix doesn't have uniform scaling\n");
Matrix3 rotationScaling2 = rotation*Matrix3::scaling(Vector2::yScale(3.5f)); CORRADE_COMPARE(rotationScaling2, Matrix2());
CORRADE_COMPARE(o.str(), "Math::Matrix3::rotation(): the matrix doesn't have uniform scaling\n"); }
CORRADE_COMPARE(rotationScaling2, Matrix3(Matrix3::Zero));
} void Matrix3Test::uniformScalingPart() {
const Matrix3 rotation = Matrix3::rotation(Deg(-74.0f));
/* Test uniform scaling */
CORRADE_COMPARE((rotation*Matrix3::scaling(Vector2(3.0f))).uniformScaling(), 3.0f);
/* Fails on non-uniform scaling */
std::ostringstream o;
Error::setOutput(&o);
const Float nonUniformScaling = (rotation*Matrix3::scaling(Vector2::yScale(3.0f))).uniformScaling();
CORRADE_COMPARE(o.str(), "Math::Matrix3::uniformScaling(): the matrix doesn't have uniform scaling\n");
CORRADE_COMPARE(nonUniformScaling, 0.0f);
} }
void Matrix3Test::vectorParts() { void Matrix3Test::vectorParts() {

31
src/Math/Test/Matrix4Test.cpp

@ -85,6 +85,7 @@ class Matrix4Test: public Corrade::TestSuite::Tester {
void rotationScalingPart(); void rotationScalingPart();
void rotationNormalizedPart(); void rotationNormalizedPart();
void rotationPart(); void rotationPart();
void uniformScalingPart();
void vectorParts(); void vectorParts();
void invertedRigid(); void invertedRigid();
void transform(); void transform();
@ -125,6 +126,7 @@ Matrix4Test::Matrix4Test() {
&Matrix4Test::rotationScalingPart, &Matrix4Test::rotationScalingPart,
&Matrix4Test::rotationNormalizedPart, &Matrix4Test::rotationNormalizedPart,
&Matrix4Test::rotationPart, &Matrix4Test::rotationPart,
&Matrix4Test::uniformScalingPart,
&Matrix4Test::vectorParts, &Matrix4Test::vectorParts,
&Matrix4Test::invertedRigid, &Matrix4Test::invertedRigid,
&Matrix4Test::transform, &Matrix4Test::transform,
@ -408,21 +410,32 @@ void Matrix4Test::rotationPart() {
CORRADE_COMPARE(rotationTranslationPart, expectedRotationPart); CORRADE_COMPARE(rotationTranslationPart, expectedRotationPart);
/* Test uniform scaling */ /* Test uniform scaling */
Matrix4 rotationScaling = rotation*Matrix4::scaling(Vector3(9.0f)); Matrix4 rotationScaling = rotation*Matrix4::scaling(Vector3(3.0f));
Matrix3 rotationScalingPart = rotationScaling.rotation(); Matrix3 rotationScalingPart = rotationScaling.rotation();
CORRADE_COMPARE(rotationScalingPart.determinant(), 1.0f); CORRADE_COMPARE(rotationScalingPart.determinant(), 1.0f);
CORRADE_COMPARE(rotationScalingPart*rotationScalingPart.transposed(), Matrix3()); CORRADE_COMPARE(rotationScalingPart*rotationScalingPart.transposed(), Matrix3());
CORRADE_COMPARE(rotationScalingPart, expectedRotationPart); CORRADE_COMPARE(rotationScalingPart, expectedRotationPart);
/* Fails on non-uniform scaling */ /* Fails on non-uniform scaling */
{ std::ostringstream o;
CORRADE_EXPECT_FAIL("Assertion on uniform scaling is not implemented yet."); Error::setOutput(&o);
std::ostringstream o; Matrix3 rotationScaling2 = (rotation*Matrix4::scaling(Vector3::yScale(3.5f))).rotation();
Error::setOutput(&o); CORRADE_COMPARE(o.str(), "Math::Matrix4::rotation(): the matrix doesn't have uniform scaling\n");
Matrix4 rotationScaling2 = rotation*Matrix4::scaling(Vector3::yScale(3.5f)); CORRADE_COMPARE(rotationScaling2, Matrix3());
CORRADE_COMPARE(o.str(), "Math::Matrix4::rotation(): the matrix doesn't have uniform scaling\n"); }
CORRADE_COMPARE(rotationScaling2, Matrix4(Matrix4::Zero));
} void Matrix4Test::uniformScalingPart() {
const Matrix4 rotation = Matrix4::rotation(Deg(-74.0f), Vector3(-1.0f, 2.0f, 2.0f).normalized());
/* Test uniform scaling */
CORRADE_COMPARE((rotation*Matrix4::scaling(Vector3(3.0f))).uniformScaling(), 3.0f);
/* Fails on non-uniform scaling */
std::ostringstream o;
Error::setOutput(&o);
const Float nonUniformScaling = (rotation*Matrix4::scaling(Vector3::yScale(3.0f))).uniformScaling();
CORRADE_COMPARE(o.str(), "Math::Matrix4::uniformScaling(): the matrix doesn't have uniform scaling\n");
CORRADE_COMPARE(nonUniformScaling, 0.0f);
} }
void Matrix4Test::vectorParts() { void Matrix4Test::vectorParts() {

40
src/Math/instantiation.cpp

@ -105,6 +105,46 @@ template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const Quate
template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const Quaternion<Double>&); template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const Quaternion<Double>&);
#endif #endif
/* Check proper size of GL types */
static_assert(sizeof(Vector<2, Float>) == 8, "Improper size of 2-element Float vector");
static_assert(sizeof(Vector<3, Float>) == 12, "Improper size of 3-element Float vector");
static_assert(sizeof(Vector<4, Float>) == 16, "Improper size of 4-element Float vector");
static_assert(sizeof(Vector<2, Int>) == 8, "Improper size of 2-element Int vector");
static_assert(sizeof(Vector<3, Int>) == 12, "Improper size of 3-element Int vector");
static_assert(sizeof(Vector<4, Int>) == 16, "Improper size of 4-element Int vector");
static_assert(sizeof(Vector<2, UnsignedInt>) == 8, "Improper size of 2-element UnsignedInt vector");
static_assert(sizeof(Vector<3, UnsignedInt>) == 12, "Improper size of 3-element UnsignedInt vector");
static_assert(sizeof(Vector<4, UnsignedInt>) == 16, "Improper size of 4-element UnsignedInt vector");
#ifndef MAGNUM_TARGET_GLES
static_assert(sizeof(Vector<2, Double>) == 16, "Improper size of 2-element Double vector");
static_assert(sizeof(Vector<3, Double>) == 24, "Improper size of 3-element Double vector");
static_assert(sizeof(Vector<4, Double>) == 32, "Improper size of 4-element Double vector");
#endif
static_assert(sizeof(RectangularMatrix<2, 2, Float>) == 16, "Improper size of 2x2 Float matrix");
static_assert(sizeof(RectangularMatrix<3, 3, Float>) == 36, "Improper size of 3x3 Float matrix");
static_assert(sizeof(RectangularMatrix<4, 4, Float>) == 64, "Improper size of 4x4 Float matrix");
#ifndef MAGNUM_TARGET_GLES
static_assert(sizeof(RectangularMatrix<2, 2, Double>) == 32, "Improper size of 2x2 Double matrix");
static_assert(sizeof(RectangularMatrix<3, 3, Double>) == 72, "Improper size of 3x3 Double matrix");
static_assert(sizeof(RectangularMatrix<4, 4, Double>) == 128, "Improper size of 4x4 Double matrix");
#endif
static_assert(sizeof(RectangularMatrix<2, 3, Float>) == 24, "Improper size of 2x3 Float matrix");
static_assert(sizeof(RectangularMatrix<3, 2, Float>) == 24, "Improper size of 3x2 Float matrix");
static_assert(sizeof(RectangularMatrix<2, 4, Float>) == 32, "Improper size of 2x4 Float matrix");
static_assert(sizeof(RectangularMatrix<4, 2, Float>) == 32, "Improper size of 4x2 Float matrix");
static_assert(sizeof(RectangularMatrix<3, 4, Float>) == 48, "Improper size of 3x4 Float matrix");
static_assert(sizeof(RectangularMatrix<4, 3, Float>) == 48, "Improper size of 4x3 Float matrix");
#ifndef MAGNUM_TARGET_GLES
static_assert(sizeof(RectangularMatrix<2, 3, Double>) == 48, "Improper size of 2x3 Double matrix");
static_assert(sizeof(RectangularMatrix<3, 2, Double>) == 48, "Improper size of 3x2 Double matrix");
static_assert(sizeof(RectangularMatrix<2, 4, Double>) == 64, "Improper size of 2x4 Double matrix");
static_assert(sizeof(RectangularMatrix<4, 2, Double>) == 64, "Improper size of 4x2 Double matrix");
static_assert(sizeof(RectangularMatrix<3, 4, Double>) == 96, "Improper size of 3x4 Double matrix");
static_assert(sizeof(RectangularMatrix<4, 3, Double>) == 96, "Improper size of 4x3 Double matrix");
#endif
template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const RectangularMatrix<2, 2, Float>&); template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const RectangularMatrix<2, 2, Float>&);
template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const RectangularMatrix<3, 3, Float>&); template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const RectangularMatrix<3, 3, Float>&);
template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const RectangularMatrix<4, 4, Float>&); template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug, const RectangularMatrix<4, 4, Float>&);

108
src/Mesh.cpp

@ -60,9 +60,9 @@ std::size_t Mesh::indexSize(IndexType type) {
Mesh::Mesh(Primitive primitive): _primitive(primitive), _vertexCount(0), _indexCount(0) Mesh::Mesh(Primitive primitive): _primitive(primitive), _vertexCount(0), _indexCount(0)
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
, indexStart(0), indexEnd(0) , _indexStart(0), _indexEnd(0)
#endif #endif
, indexOffset(0), indexType(IndexType::UnsignedInt), indexBuffer(nullptr) , _indexOffset(0), _indexType(IndexType::UnsignedInt), _indexBuffer(nullptr)
{ {
(this->*createImplementation)(); (this->*createImplementation)();
} }
@ -70,49 +70,49 @@ Mesh::Mesh(Primitive primitive): _primitive(primitive), _vertexCount(0), _indexC
Mesh::~Mesh() { Mesh::~Mesh() {
/* Remove current vao from the state */ /* Remove current vao from the state */
GLuint& current = Context::current()->state().mesh->currentVAO; GLuint& current = Context::current()->state().mesh->currentVAO;
if(current == vao) current = 0; if(current == _id) current = 0;
(this->*destroyImplementation)(); (this->*destroyImplementation)();
} }
Mesh::Mesh(Mesh&& other): vao(other.vao), _primitive(other._primitive), _vertexCount(other._vertexCount), _indexCount(other._indexCount) Mesh::Mesh(Mesh&& other): _id(other._id), _primitive(other._primitive), _vertexCount(other._vertexCount), _indexCount(other._indexCount)
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
, indexStart(other.indexStart), indexEnd(other.indexEnd) , _indexStart(other._indexStart), _indexEnd(other._indexEnd)
#endif #endif
, indexOffset(other.indexOffset), indexType(other.indexType), indexBuffer(other.indexBuffer), attributes(std::move(other.attributes)) , _indexOffset(other._indexOffset), _indexType(other._indexType), _indexBuffer(other._indexBuffer), _attributes(std::move(other._attributes))
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
, integerAttributes(std::move(other.integerAttributes)) , _integerAttributes(std::move(other._integerAttributes))
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
, longAttributes(std::move(other.longAttributes)) , _longAttributes(std::move(other._longAttributes))
#endif #endif
#endif #endif
{ {
other.vao = 0; other._id = 0;
} }
Mesh& Mesh::operator=(Mesh&& other) { Mesh& Mesh::operator=(Mesh&& other) {
(this->*destroyImplementation)(); (this->*destroyImplementation)();
vao = other.vao; _id = other._id;
_primitive = other._primitive; _primitive = other._primitive;
_vertexCount = other._vertexCount; _vertexCount = other._vertexCount;
_indexCount = other._indexCount; _indexCount = other._indexCount;
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
indexStart = other.indexStart; _indexStart = other._indexStart;
indexEnd = other.indexEnd; _indexEnd = other._indexEnd;
#endif #endif
indexOffset = other.indexOffset; _indexOffset = other._indexOffset;
indexType = other.indexType; _indexType = other._indexType;
indexBuffer = other.indexBuffer; _indexBuffer = other._indexBuffer;
attributes = std::move(other.attributes); _attributes = std::move(other._attributes);
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
integerAttributes = std::move(other.integerAttributes); _integerAttributes = std::move(other._integerAttributes);
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
longAttributes = std::move(other.longAttributes); _longAttributes = std::move(other._longAttributes);
#endif #endif
#endif #endif
other.vao = 0; other._id = 0;
return *this; return *this;
} }
@ -120,14 +120,14 @@ Mesh& Mesh::operator=(Mesh&& other) {
Mesh& Mesh::setIndexBuffer(Buffer& buffer, GLintptr offset, IndexType type, UnsignedInt start, UnsignedInt end) { Mesh& Mesh::setIndexBuffer(Buffer& buffer, GLintptr offset, IndexType type, UnsignedInt start, UnsignedInt end) {
#ifdef CORRADE_TARGET_NACL #ifdef CORRADE_TARGET_NACL
CORRADE_ASSERT(buffer.targetHint() == Buffer::Target::ElementArray, CORRADE_ASSERT(buffer.targetHint() == Buffer::Target::ElementArray,
"Mesh::setIndexBuffer(): the buffer has unexpected target hint, expected" << Buffer::Target::ElementArray << "but got" << buffer.targetHint(), this); "Mesh::setIndexBuffer(): the buffer has unexpected target hint, expected" << Buffer::Target::ElementArray << "but got" << buffer.targetHint(), *this);
#endif #endif
indexOffset = offset; _indexOffset = offset;
indexType = type; _indexType = type;
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
indexStart = start; _indexStart = start;
indexEnd = end; _indexEnd = end;
#else #else
static_cast<void>(start); static_cast<void>(start);
static_cast<void>(end); static_cast<void>(end);
@ -136,25 +136,25 @@ Mesh& Mesh::setIndexBuffer(Buffer& buffer, GLintptr offset, IndexType type, Unsi
return *this; return *this;
} }
void Mesh::draw() { void Mesh::drawInternal(Int firstVertex, Int vertexCount, GLintptr indexOffset, Int indexCount, Int indexStart, Int indexEnd) {
/* Nothing to draw */ /* Nothing to draw */
if(!_vertexCount && !_indexCount) return; if(!vertexCount && !indexCount) return;
(this->*bindImplementation)(); (this->*bindImplementation)();
/* Non-indexed mesh */ /* Non-indexed mesh */
if(!_indexCount) if(!indexCount)
glDrawArrays(static_cast<GLenum>(_primitive), 0, _vertexCount); glDrawArrays(static_cast<GLenum>(_primitive), firstVertex, vertexCount);
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
/* Indexed mesh with specified range */ /* Indexed mesh with specified range */
else if(indexEnd) else if(indexEnd)
glDrawRangeElements(static_cast<GLenum>(_primitive), indexStart, indexEnd, _indexCount, static_cast<GLenum>(indexType), reinterpret_cast<GLvoid*>(indexOffset)); glDrawRangeElements(static_cast<GLenum>(_primitive), indexStart, indexEnd, indexCount, static_cast<GLenum>(_indexType), reinterpret_cast<GLvoid*>(indexOffset));
#endif #endif
/* Indexed mesh without specified range */ /* Indexed mesh without specified range */
else else
glDrawElements(static_cast<GLenum>(_primitive), _indexCount, static_cast<GLenum>(indexType), reinterpret_cast<GLvoid*>(indexOffset)); glDrawElements(static_cast<GLenum>(_primitive), indexCount, static_cast<GLenum>(_indexType), reinterpret_cast<GLvoid*>(indexOffset));
(this->*unbindImplementation)(); (this->*unbindImplementation)();
} }
@ -226,7 +226,7 @@ void Mesh::createImplementationDefault() {}
void Mesh::createImplementationVAO() { void Mesh::createImplementationVAO() {
/** @todo Get some extension wrangler instead to avoid linker errors to glGenVertexArrays() on ES2 */ /** @todo Get some extension wrangler instead to avoid linker errors to glGenVertexArrays() on ES2 */
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
glGenVertexArrays(1, &vao); glGenVertexArrays(1, &_id);
#endif #endif
} }
@ -235,7 +235,7 @@ void Mesh::destroyImplementationDefault() {}
void Mesh::destroyImplementationVAO() { void Mesh::destroyImplementationVAO() {
/** @todo Get some extension wrangler instead to avoid linker errors to glDeleteVertexArrays() on ES2 */ /** @todo Get some extension wrangler instead to avoid linker errors to glDeleteVertexArrays() on ES2 */
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
if(vao) glDeleteVertexArrays(1, &vao); if(_id) glDeleteVertexArrays(1, &_id);
#endif #endif
} }
@ -245,7 +245,7 @@ void Mesh::attributePointerImplementationDefault(const Attribute& attribute) {
"Mesh::addVertexBuffer(): the buffer has unexpected target hint, expected" << Buffer::Target::Array << "but got" << attribute.buffer->targetHint(), ); "Mesh::addVertexBuffer(): the buffer has unexpected target hint, expected" << Buffer::Target::Array << "but got" << attribute.buffer->targetHint(), );
#endif #endif
attributes.push_back(attribute); _attributes.push_back(attribute);
} }
void Mesh::attributePointerImplementationVAO(const Attribute& attribute) { void Mesh::attributePointerImplementationVAO(const Attribute& attribute) {
@ -254,57 +254,57 @@ void Mesh::attributePointerImplementationVAO(const Attribute& attribute) {
"Mesh::addVertexBuffer(): the buffer has unexpected target hint, expected" << Buffer::Target::Array << "but got" << attribute.buffer->targetHint(), ); "Mesh::addVertexBuffer(): the buffer has unexpected target hint, expected" << Buffer::Target::Array << "but got" << attribute.buffer->targetHint(), );
#endif #endif
bindVAO(vao); bindVAO(_id);
vertexAttribPointer(attribute); vertexAttribPointer(attribute);
} }
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
void Mesh::attributePointerImplementationDSA(const Attribute& attribute) { void Mesh::attributePointerImplementationDSA(const Attribute& attribute) {
glEnableVertexArrayAttribEXT(vao, attribute.location); glEnableVertexArrayAttribEXT(_id, attribute.location);
glVertexArrayVertexAttribOffsetEXT(vao, attribute.buffer->id(), attribute.location, attribute.size, attribute.type, attribute.normalized, attribute.stride, attribute.offset); glVertexArrayVertexAttribOffsetEXT(_id, attribute.buffer->id(), attribute.location, attribute.size, attribute.type, attribute.normalized, attribute.stride, attribute.offset);
} }
#endif #endif
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
void Mesh::attributePointerImplementationDefault(const IntegerAttribute& attribute) { void Mesh::attributePointerImplementationDefault(const IntegerAttribute& attribute) {
integerAttributes.push_back(attribute); _integerAttributes.push_back(attribute);
} }
void Mesh::attributePointerImplementationVAO(const IntegerAttribute& attribute) { void Mesh::attributePointerImplementationVAO(const IntegerAttribute& attribute) {
bindVAO(vao); bindVAO(_id);
vertexAttribPointer(attribute); vertexAttribPointer(attribute);
} }
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
void Mesh::attributePointerImplementationDSA(const IntegerAttribute& attribute) { void Mesh::attributePointerImplementationDSA(const IntegerAttribute& attribute) {
glEnableVertexArrayAttribEXT(vao, attribute.location); glEnableVertexArrayAttribEXT(_id, attribute.location);
glVertexArrayVertexAttribIOffsetEXT(vao, attribute.buffer->id(), attribute.location, attribute.size, attribute.type, attribute.stride, attribute.offset); glVertexArrayVertexAttribIOffsetEXT(_id, attribute.buffer->id(), attribute.location, attribute.size, attribute.type, attribute.stride, attribute.offset);
} }
#endif #endif
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
void Mesh::attributePointerImplementationDefault(const LongAttribute& attribute) { void Mesh::attributePointerImplementationDefault(const LongAttribute& attribute) {
longAttributes.push_back(attribute); _longAttributes.push_back(attribute);
} }
void Mesh::attributePointerImplementationVAO(const LongAttribute& attribute) { void Mesh::attributePointerImplementationVAO(const LongAttribute& attribute) {
bindVAO(vao); bindVAO(_id);
vertexAttribPointer(attribute); vertexAttribPointer(attribute);
} }
void Mesh::attributePointerImplementationDSA(const LongAttribute& attribute) { void Mesh::attributePointerImplementationDSA(const LongAttribute& attribute) {
glEnableVertexArrayAttribEXT(vao, attribute.location); glEnableVertexArrayAttribEXT(_id, attribute.location);
glVertexArrayVertexAttribLOffsetEXT(vao, attribute.buffer->id(), attribute.location, attribute.size, attribute.type, attribute.stride, attribute.offset); glVertexArrayVertexAttribLOffsetEXT(_id, attribute.buffer->id(), attribute.location, attribute.size, attribute.type, attribute.stride, attribute.offset);
} }
#endif #endif
#endif #endif
void Mesh::bindIndexBufferImplementationDefault(Buffer& buffer) { void Mesh::bindIndexBufferImplementationDefault(Buffer& buffer) {
indexBuffer = &buffer; _indexBuffer = &buffer;
} }
void Mesh::bindIndexBufferImplementationVAO(Buffer& buffer) { void Mesh::bindIndexBufferImplementationVAO(Buffer& buffer) {
bindVAO(vao); bindVAO(_id);
/* Reset ElementArray binding to force explicit glBindBuffer call later */ /* Reset ElementArray binding to force explicit glBindBuffer call later */
/** @todo Do this cleaner way */ /** @todo Do this cleaner way */
@ -315,37 +315,37 @@ void Mesh::bindIndexBufferImplementationVAO(Buffer& buffer) {
void Mesh::bindImplementationDefault() { void Mesh::bindImplementationDefault() {
/* Specify vertex attributes */ /* Specify vertex attributes */
for(auto it = attributes.begin(); it != attributes.end(); ++it) for(auto it = _attributes.begin(); it != _attributes.end(); ++it)
vertexAttribPointer(*it); vertexAttribPointer(*it);
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
for(auto it = integerAttributes.begin(); it != integerAttributes.end(); ++it) for(auto it = _integerAttributes.begin(); it != _integerAttributes.end(); ++it)
vertexAttribPointer(*it); vertexAttribPointer(*it);
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
for(auto it = longAttributes.begin(); it != longAttributes.end(); ++it) for(auto it = _longAttributes.begin(); it != _longAttributes.end(); ++it)
vertexAttribPointer(*it); vertexAttribPointer(*it);
#endif #endif
#endif #endif
/* Bind index buffer, if the mesh is indexed */ /* Bind index buffer, if the mesh is indexed */
if(_indexCount) indexBuffer->bind(Buffer::Target::ElementArray); if(_indexCount) _indexBuffer->bind(Buffer::Target::ElementArray);
} }
void Mesh::bindImplementationVAO() { void Mesh::bindImplementationVAO() {
bindVAO(vao); bindVAO(_id);
} }
void Mesh::unbindImplementationDefault() { void Mesh::unbindImplementationDefault() {
for(auto it = attributes.begin(); it != attributes.end(); ++it) for(auto it = _attributes.begin(); it != _attributes.end(); ++it)
glDisableVertexAttribArray(it->location); glDisableVertexAttribArray(it->location);
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
for(auto it = integerAttributes.begin(); it != integerAttributes.end(); ++it) for(auto it = _integerAttributes.begin(); it != _integerAttributes.end(); ++it)
glDisableVertexAttribArray(it->location); glDisableVertexAttribArray(it->location);
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
for(auto it = longAttributes.begin(); it != longAttributes.end(); ++it) for(auto it = _longAttributes.begin(); it != _longAttributes.end(); ++it)
glDisableVertexAttribArray(it->location); glDisableVertexAttribArray(it->location);
#endif #endif
#endif #endif

37
src/Mesh.h

@ -240,6 +240,7 @@ for more information.
*/ */
class MAGNUM_EXPORT Mesh { class MAGNUM_EXPORT Mesh {
friend class Context; friend class Context;
friend class MeshView;
Mesh(const Mesh&) = delete; Mesh(const Mesh&) = delete;
Mesh& operator=(const Mesh&) = delete; Mesh& operator=(const Mesh&) = delete;
@ -335,7 +336,11 @@ class MAGNUM_EXPORT Mesh {
UnsignedInt = GL_UNSIGNED_INT UnsignedInt = GL_UNSIGNED_INT
}; };
/** @brief Size of given index type */ /**
* @brief Size of given index type
*
* @see indexSize() const
*/
static std::size_t indexSize(IndexType type); static std::size_t indexSize(IndexType type);
/** /**
@ -362,6 +367,13 @@ class MAGNUM_EXPORT Mesh {
/** @brief Move assignment */ /** @brief Move assignment */
Mesh& operator=(Mesh&& other); Mesh& operator=(Mesh&& other);
/**
* @brief Index size
*
* @see indexSize(IndexType)
*/
std::size_t indexSize() const { return indexSize(_indexType); }
/** @brief Primitive type */ /** @brief Primitive type */
Primitive primitive() const { return _primitive; } Primitive primitive() const { return _primitive; }
@ -584,7 +596,9 @@ class MAGNUM_EXPORT Mesh {
* or @fn_gl{BindVertexArray} (if @extension{APPLE,vertex_array_object} * or @fn_gl{BindVertexArray} (if @extension{APPLE,vertex_array_object}
* is available), @fn_gl{DrawArrays} or @fn_gl{DrawElements}/@fn_gl{DrawRangeElements}. * is available), @fn_gl{DrawArrays} or @fn_gl{DrawElements}/@fn_gl{DrawRangeElements}.
*/ */
void draw(); void draw() {
drawInternal(0, _vertexCount, _indexOffset, _indexCount, _indexStart, _indexEnd);
}
private: private:
#ifndef DOXYGEN_GENERATING_OUTPUT #ifndef DOXYGEN_GENERATING_OUTPUT
@ -708,6 +722,8 @@ class MAGNUM_EXPORT Mesh {
#endif #endif
#endif #endif
void drawInternal(Int firstVertex, Int vertexCount, GLintptr indexOffset, Int indexCount, Int indexStart, Int indexEnd);
typedef void(Mesh::*CreateImplementation)(); typedef void(Mesh::*CreateImplementation)();
void MAGNUM_LOCAL createImplementationDefault(); void MAGNUM_LOCAL createImplementationDefault();
void MAGNUM_LOCAL createImplementationVAO(); void MAGNUM_LOCAL createImplementationVAO();
@ -759,21 +775,21 @@ class MAGNUM_EXPORT Mesh {
void MAGNUM_LOCAL unbindImplementationVAO(); void MAGNUM_LOCAL unbindImplementationVAO();
static MAGNUM_LOCAL UnbindImplementation unbindImplementation; static MAGNUM_LOCAL UnbindImplementation unbindImplementation;
GLuint vao; GLuint _id;
Primitive _primitive; Primitive _primitive;
Int _vertexCount, _indexCount; Int _vertexCount, _indexCount;
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
UnsignedInt indexStart, indexEnd; UnsignedInt _indexStart, _indexEnd;
#endif #endif
GLintptr indexOffset; GLintptr _indexOffset;
IndexType indexType; IndexType _indexType;
Buffer* indexBuffer; Buffer* _indexBuffer;
std::vector<Attribute> attributes; std::vector<Attribute> _attributes;
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
std::vector<IntegerAttribute> integerAttributes; std::vector<IntegerAttribute> _integerAttributes;
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
std::vector<LongAttribute> longAttributes; std::vector<LongAttribute> _longAttributes;
#endif #endif
#endif #endif
}; };
@ -792,7 +808,6 @@ template<class ...T> inline Mesh& Mesh::addVertexBuffer(Buffer& buffer, GLintptr
return *this; return *this;
} }
} }
namespace Corrade { namespace Utility { namespace Corrade { namespace Utility {

6
src/MeshTools/CMakeLists.txt

@ -77,5 +77,11 @@ if(BUILD_TESTS)
set_target_properties(MagnumMeshToolsTestLib PROPERTIES COMPILE_FLAGS "-DCORRADE_GRACEFUL_ASSERT -DMagnumMeshTools_EXPORTS") set_target_properties(MagnumMeshToolsTestLib PROPERTIES COMPILE_FLAGS "-DCORRADE_GRACEFUL_ASSERT -DMagnumMeshTools_EXPORTS")
target_link_libraries(MagnumMeshToolsTestLib Magnum) target_link_libraries(MagnumMeshToolsTestLib Magnum)
# 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 MagnumMeshToolsTestLib DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR})
endif()
add_subdirectory(Test) add_subdirectory(Test)
endif() endif()

43
src/MeshView.cpp

@ -0,0 +1,43 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 "MeshView.h"
#include "Mesh.h"
namespace Magnum {
MeshView& MeshView::setIndexRange(Int first, Int count, UnsignedInt start, UnsignedInt end) {
_indexOffset = first*_original->indexSize();
_indexCount = count;
_indexStart = start;
_indexEnd = end;
return *this;
}
void MeshView::draw() {
_original->drawInternal(_firstVertex, _vertexCount, _indexOffset, _indexCount, _indexStart, _indexEnd);
}
}

139
src/MeshView.h

@ -0,0 +1,139 @@
#ifndef Magnum_MeshView_h
#define Magnum_MeshView_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 Magnum::MeshView
*/
#include "Magnum.h"
#include "magnumVisibility.h"
#ifndef DOXYGEN_GENERATING_OUTPUT
typedef std::ptrdiff_t GLintptr;
#endif
namespace Magnum {
/**
@brief %Mesh view
Allows different interpretation of given @ref Mesh data via different vertex or
index count and offset. It is then possible to reuse one mesh buffer
configuration for different views. %Mesh primitive, index type, attribute
bindings and attached buffers are reused from original mesh.
The same rules as in Mesh apply, i.e. if the view has non-zero index count, it
is treated as indexed mesh, otherwise it is treated as non-indexed mesh. If
both index and vertex count is zero, the view is treated as empty and no draw
commands are issued when calling draw().
You must ensure that the original mesh remains available for whole view
lifetime.
@todo Might cause issues when there are more data than just indices in index
buffer (wrongly computed offset)
*/
class MAGNUM_EXPORT MeshView {
public:
/**
* @brief Constructor
* @param original Original, already configured mesh
*/
explicit MeshView(Mesh& original);
/** @brief Copy constructor */
MeshView(const MeshView& other) = default;
/** @brief Movement is not allowed */
MeshView(MeshView&& other) = delete;
/** @brief Copy assignment */
MeshView& operator=(const MeshView&) = default;
/** @brief Movement is not allowed */
MeshView& operator=(MeshView&& other) = delete;
/**
* @brief Set vertex range
* @param first First vertex
* @param count Vertex count
* @return Reference to self (for method chaining)
*
* Default is zero @p offset and zero @p count. If index range is
* non-zero, vertex range is ignored, see main class documentation for
* more information.
*/
MeshView& setVertexRange(Int first, Int count) {
_firstVertex = first;
_vertexCount = count;
return *this;
}
/**
* @brief Set index range
* @param first First index
* @param count Index count
* @param start Minimum array index contained in the buffer
* @param end Maximum array index contained in the buffer
* @return Reference to self (for method chaining)
*
* Specifying `0` for both @p start and @p end behaves the same as
* @ref setIndexRange(Int, Int).
*/
MeshView& setIndexRange(Int first, Int count, UnsignedInt start, UnsignedInt end);
/**
* @brief Set index range
* @param first First index
* @param count Index count
* @return Reference to self (for method chaining)
*
* Prefer to use @ref setIndexRange(Int, Int, UnsignedInt, UnsignedInt)
* for better performance.
*/
MeshView& setIndexRange(Int first, Int count) {
return setIndexRange(first, count, 0, 0);
}
/**
* @brief Draw the mesh
*
* See @ref Mesh::draw() for more information.
*/
void draw();
private:
Mesh* _original;
Int _firstVertex, _vertexCount, _indexCount;
GLintptr _indexOffset;
UnsignedInt _indexStart, _indexEnd;
};
inline MeshView::MeshView(Mesh& original): _original(&original), _firstVertex(0), _vertexCount(0), _indexCount(0), _indexOffset(0), _indexStart(0), _indexEnd(0) {}
}
#endif

77
src/Primitives/Capsule.cpp

@ -25,20 +25,77 @@
#include "Capsule.h" #include "Capsule.h"
#include "Math/Vector3.h" #include "Math/Vector3.h"
#include "Math/Functions.h"
#include "Primitives/Implementation/Spheroid.h" #include "Primitives/Implementation/Spheroid.h"
#include "Primitives/Implementation/WireframeSpheroid.h" #include "Primitives/Implementation/WireframeSpheroid.h"
#include "Trade/MeshData2D.h"
#include "Trade/MeshData3D.h" #include "Trade/MeshData3D.h"
namespace Magnum { namespace Primitives { namespace Magnum { namespace Primitives {
Trade::MeshData3D Capsule::solid(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float length, TextureCoords textureCoords) { Trade::MeshData2D Capsule2D::wireframe(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, Float halfLength) {
CORRADE_ASSERT(hemisphereRings >= 1 && cylinderRings >= 1, "Capsule must have at least one hemisphere ring, one cylinder ring and three segments", Trade::MeshData2D(Mesh::Primitive::Lines, {}, {}, {}));
std::vector<Vector2> positions;
positions.reserve(hemisphereRings*4+2+(cylinderRings-1)*2);
const Rad angleIncrement(Constants::pi()/(2.0f*hemisphereRings));
const Float cylinderIncrement = 2.0f*halfLength/cylinderRings;
/* Bottom cap vertex */
positions.emplace_back(0.0f, -halfLength-1.0f);
/* Bottom hemisphere */
for(UnsignedInt i = 0; i != hemisphereRings; ++i) {
const Rad angle((i+1)*angleIncrement);
const Float x = Math::sin(angle);
const Float y = -Math::cos(angle)-halfLength;
positions.insert(positions.end(), {{-x, y}, {x, y}});
}
/* Cylinder (bottom and top vertices are done within caps */
for(UnsignedInt i = 0; i != cylinderRings-1; ++i) {
const Float y = (i+1)*cylinderIncrement-halfLength;
positions.insert(positions.end(), {{-1.0f, y}, {1.0f, y}});
}
/* Top hemisphere */
for(UnsignedInt i = 0; i != hemisphereRings; ++i) {
const Rad angle(i*angleIncrement);
const Float x = Math::cos(angle);
const Float y = Math::sin(angle)+halfLength;
positions.insert(positions.end(), {{-x, y}, {x, y}});
}
/* Top cap vertex */
positions.emplace_back(0.0f, halfLength+1.0f);
std::vector<UnsignedInt> indices;
indices.reserve(hemisphereRings*8+cylinderRings*4);
/* Bottom cap indices */
indices.insert(indices.end(), {0, 1, 0, 2});
/* Side indices */
for(UnsignedInt i = 0; i != cylinderRings+hemisphereRings*2-2; ++i)
indices.insert(indices.end(), {i*2+1, i*2+3,
i*2+2, i*2+4});
/* Top cap indices */
indices.insert(indices.end(),
{UnsignedInt(positions.size())-3, UnsignedInt(positions.size())-1,
UnsignedInt(positions.size())-2, UnsignedInt(positions.size())-1});
return Trade::MeshData2D(Mesh::Primitive::Lines, std::move(indices), {std::move(positions)}, {});
}
Trade::MeshData3D Capsule3D::solid(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float halfLength, TextureCoords textureCoords) {
CORRADE_ASSERT(hemisphereRings >= 1 && cylinderRings >= 1 && segments >= 3, "Capsule must have at least one hemisphere ring, one cylinder ring and three segments", Trade::MeshData3D(Mesh::Primitive::Triangles, {}, {}, {}, {})); CORRADE_ASSERT(hemisphereRings >= 1 && cylinderRings >= 1 && segments >= 3, "Capsule must have at least one hemisphere ring, one cylinder ring and three segments", Trade::MeshData3D(Mesh::Primitive::Triangles, {}, {}, {}, {}));
Implementation::Spheroid capsule(segments, textureCoords == TextureCoords::Generate ? Implementation::Spheroid capsule(segments, textureCoords == TextureCoords::Generate ?
Implementation::Spheroid::TextureCoords::Generate : Implementation::Spheroid::TextureCoords::Generate :
Implementation::Spheroid::TextureCoords::DontGenerate); Implementation::Spheroid::TextureCoords::DontGenerate);
Float height = 2.0f+length; Float height = 2.0f+2.0f*halfLength;
Float hemisphereTextureCoordsVIncrement = 1.0f/(hemisphereRings*height); Float hemisphereTextureCoordsVIncrement = 1.0f/(hemisphereRings*height);
Rad hemisphereRingAngleIncrement(Constants::pi()/(2*hemisphereRings)); Rad hemisphereRingAngleIncrement(Constants::pi()/(2*hemisphereRings));
@ -46,13 +103,13 @@ Trade::MeshData3D Capsule::solid(UnsignedInt hemisphereRings, UnsignedInt cylind
capsule.capVertex(-height/2, -1.0f, 0.0f); capsule.capVertex(-height/2, -1.0f, 0.0f);
/* Rings of bottom hemisphere */ /* Rings of bottom hemisphere */
capsule.hemisphereVertexRings(hemisphereRings-1, -length/2, -Rad(Constants::pi())/2+hemisphereRingAngleIncrement, hemisphereRingAngleIncrement, hemisphereTextureCoordsVIncrement, hemisphereTextureCoordsVIncrement); capsule.hemisphereVertexRings(hemisphereRings-1, -halfLength, -Rad(Constants::pi())/2+hemisphereRingAngleIncrement, hemisphereRingAngleIncrement, hemisphereTextureCoordsVIncrement, hemisphereTextureCoordsVIncrement);
/* Rings of cylinder */ /* Rings of cylinder */
capsule.cylinderVertexRings(cylinderRings+1, -length/2, length/cylinderRings, 1.0f/height, length/(cylinderRings*height)); capsule.cylinderVertexRings(cylinderRings+1, -halfLength, 2.0f*halfLength/cylinderRings, 1.0f/height, 2.0f*halfLength/(cylinderRings*height));
/* Rings of top hemisphere */ /* Rings of top hemisphere */
capsule.hemisphereVertexRings(hemisphereRings-1, length/2, hemisphereRingAngleIncrement, hemisphereRingAngleIncrement, (1.0f + length)/height+hemisphereTextureCoordsVIncrement, hemisphereTextureCoordsVIncrement); capsule.hemisphereVertexRings(hemisphereRings-1, halfLength, hemisphereRingAngleIncrement, hemisphereRingAngleIncrement, (1.0f + 2.0f*halfLength)/height+hemisphereTextureCoordsVIncrement, hemisphereTextureCoordsVIncrement);
/* Top cap vertex */ /* Top cap vertex */
capsule.capVertex(height/2, 1.0f, 1.0f); capsule.capVertex(height/2, 1.0f, 1.0f);
@ -65,23 +122,23 @@ Trade::MeshData3D Capsule::solid(UnsignedInt hemisphereRings, UnsignedInt cylind
return capsule.finalize(); return capsule.finalize();
} }
Trade::MeshData3D Capsule::wireframe(const UnsignedInt hemisphereRings, const UnsignedInt cylinderRings, const UnsignedInt segments, const Float length) { Trade::MeshData3D Capsule3D::wireframe(const UnsignedInt hemisphereRings, const UnsignedInt cylinderRings, const UnsignedInt segments, const Float halfLength) {
CORRADE_ASSERT(hemisphereRings >= 1 && cylinderRings >= 1 && segments >= 4 && segments%4 == 0, "Primitives::Capsule::wireframe(): improper parameters", Trade::MeshData3D(Mesh::Primitive::Lines, {}, {}, {}, {})); CORRADE_ASSERT(hemisphereRings >= 1 && cylinderRings >= 1 && segments >= 4 && segments%4 == 0, "Primitives::Capsule::wireframe(): improper parameters", Trade::MeshData3D(Mesh::Primitive::Lines, {}, {}, {}, {}));
Implementation::WireframeSpheroid capsule(segments/4); Implementation::WireframeSpheroid capsule(segments/4);
/* Bottom hemisphere */ /* Bottom hemisphere */
capsule.bottomHemisphere(-length/2, hemisphereRings); capsule.bottomHemisphere(-halfLength, hemisphereRings);
/* Cylinder */ /* Cylinder */
capsule.ring(-length/2); capsule.ring(-halfLength);
for(UnsignedInt i = 0; i != cylinderRings; ++i) { for(UnsignedInt i = 0; i != cylinderRings; ++i) {
capsule.cylinder(); capsule.cylinder();
capsule.ring(-length/2 + (i+1)*(length/cylinderRings)); capsule.ring(-halfLength + (i+1)*(2.0f*halfLength/cylinderRings));
} }
/* Top hemisphere */ /* Top hemisphere */
capsule.topHemisphere(length/2, hemisphereRings); capsule.topHemisphere(halfLength, hemisphereRings);
return capsule.finalize(); return capsule.finalize();
} }

30
src/Primitives/Capsule.h

@ -33,12 +33,32 @@
namespace Magnum { namespace Primitives { namespace Magnum { namespace Primitives {
/**
@brief 2D capsule primitive
%Cylinder of radius `1` along Y axis with hemispheres instead of caps.
*/
class MAGNUM_PRIMITIVES_EXPORT Capsule2D {
public:
/**
* @brief Wireframe capsule
* @param hemisphereRings Number of (line) rings for each hemisphere.
* Must be larger or equal to 1.
* @param cylinderRings Number of (line) rings for cylinder. Must be
* larger or equal to 1.
* @param halfLength Half the length of cylinder part
*
* Indexed @ref Mesh::Primitive "Lines".
*/
static Trade::MeshData2D wireframe(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, Float halfLength);
};
/** /**
@brief 3D capsule primitive @brief 3D capsule primitive
%Cylinder of radius `1` along Y axis with hemispheres instead of caps. %Cylinder of radius `1` along Y axis with hemispheres instead of caps.
*/ */
class MAGNUM_PRIMITIVES_EXPORT Capsule { class MAGNUM_PRIMITIVES_EXPORT Capsule3D {
public: public:
/** @brief Whether to generate texture coordinates */ /** @brief Whether to generate texture coordinates */
enum class TextureCoords: UnsignedByte { enum class TextureCoords: UnsignedByte {
@ -54,14 +74,14 @@ class MAGNUM_PRIMITIVES_EXPORT Capsule {
* larger or equal to 1. * larger or equal to 1.
* @param segments Number of (face) segments. Must be larger or * @param segments Number of (face) segments. Must be larger or
* equal to 3. * equal to 3.
* @param length Length of the capsule, excluding hemispheres. * @param halfLength Half the length of cylinder part
* @param textureCoords Whether to generate texture coordinates. * @param textureCoords Whether to generate texture coordinates.
* *
* Indexed @ref Mesh::Primitive "Triangles" with normals and optional * Indexed @ref Mesh::Primitive "Triangles" with normals and optional
* 2D texture coordinates. If texture coordinates are generated, * 2D texture coordinates. If texture coordinates are generated,
* vertices of one segment are duplicated for texture wrapping. * vertices of one segment are duplicated for texture wrapping.
*/ */
static Trade::MeshData3D solid(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float length, TextureCoords textureCoords = TextureCoords::DontGenerate); static Trade::MeshData3D solid(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float halfLength, TextureCoords textureCoords = TextureCoords::DontGenerate);
/** /**
* @brief Wireframe capsule * @brief Wireframe capsule
@ -71,11 +91,11 @@ class MAGNUM_PRIMITIVES_EXPORT Capsule {
* larger or equal to 1. * larger or equal to 1.
* @param segments Number of line segments. Must be larger or * @param segments Number of line segments. Must be larger or
* equal to 4 and multiple of 4. * equal to 4 and multiple of 4.
* @param length Length of the capsule, excluding hemispheres. * @param halfLength Half the length of cylinder part
* *
* Indexed @ref Mesh::Primitive "Lines". * Indexed @ref Mesh::Primitive "Lines".
*/ */
static Trade::MeshData3D wireframe(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float length); static Trade::MeshData3D wireframe(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float halfLength);
}; };
}} }}

24
src/Primitives/Cylinder.cpp

@ -31,27 +31,27 @@
namespace Magnum { namespace Primitives { namespace Magnum { namespace Primitives {
Trade::MeshData3D Cylinder::solid(UnsignedInt rings, UnsignedInt segments, Float length, Cylinder::Flags flags) { Trade::MeshData3D Cylinder::solid(const UnsignedInt rings, const UnsignedInt segments, const Float halfLength, const Flags flags) {
CORRADE_ASSERT(rings >= 1 && segments >= 3, "Primitives::Cylinder::solid(): cylinder must have at least one ring and three segments", Trade::MeshData3D(Mesh::Primitive::Triangles, {}, {}, {}, {})); CORRADE_ASSERT(rings >= 1 && segments >= 3, "Primitives::Cylinder::solid(): cylinder must have at least one ring and three segments", Trade::MeshData3D(Mesh::Primitive::Triangles, {}, {}, {}, {}));
Implementation::Spheroid cylinder(segments, flags & Flag::GenerateTextureCoords ? Implementation::Spheroid::TextureCoords::Generate : Implementation::Spheroid::TextureCoords::DontGenerate); Implementation::Spheroid cylinder(segments, flags & Flag::GenerateTextureCoords ? Implementation::Spheroid::TextureCoords::Generate : Implementation::Spheroid::TextureCoords::DontGenerate);
Float y = length*0.5f; const Float length = 2.0f*halfLength;
Float textureCoordsV = flags & Flag::CapEnds ? 1.0f/(length+2.0f) : 0.0f; const Float textureCoordsV = flags & Flag::CapEnds ? 1.0f/(length+2.0f) : 0.0f;
/* Bottom cap */ /* Bottom cap */
if(flags & Flag::CapEnds) { if(flags & Flag::CapEnds) {
cylinder.capVertex(-y, -1.0f, 0.0f); cylinder.capVertex(-halfLength, -1.0f, 0.0f);
cylinder.capVertexRing(-y, textureCoordsV, Vector3::yAxis(-1.0f)); cylinder.capVertexRing(-halfLength, textureCoordsV, Vector3::yAxis(-1.0f));
} }
/* Vertex rings */ /* Vertex rings */
cylinder.cylinderVertexRings(rings+1, -y, length/rings, textureCoordsV, length/(rings*(flags & Flag::CapEnds ? length + 2.0f : length))); cylinder.cylinderVertexRings(rings+1, -halfLength, length/rings, textureCoordsV, length/(rings*(flags & Flag::CapEnds ? length + 2.0f : length)));
/* Top cap */ /* Top cap */
if(flags & Flag::CapEnds) { if(flags & Flag::CapEnds) {
cylinder.capVertexRing(y, 1.0f - textureCoordsV, Vector3::yAxis(1.0f)); cylinder.capVertexRing(halfLength, 1.0f - textureCoordsV, Vector3::yAxis(1.0f));
cylinder.capVertex(y, 1.0f, 1.0f); cylinder.capVertex(halfLength, 1.0f, 1.0f);
} }
/* Faces */ /* Faces */
@ -62,16 +62,18 @@ Trade::MeshData3D Cylinder::solid(UnsignedInt rings, UnsignedInt segments, Float
return cylinder.finalize(); return cylinder.finalize();
} }
Trade::MeshData3D Cylinder::wireframe(const UnsignedInt rings, const UnsignedInt segments, const Float length) { Trade::MeshData3D Cylinder::wireframe(const UnsignedInt rings, const UnsignedInt segments, const Float halfLength) {
CORRADE_ASSERT(rings >= 1 && segments >= 4 && segments%4 == 0, "Primitives::Cylinder::wireframe(): improper parameters", Trade::MeshData3D(Mesh::Primitive::Lines, {}, {}, {}, {})); CORRADE_ASSERT(rings >= 1 && segments >= 4 && segments%4 == 0, "Primitives::Cylinder::wireframe(): improper parameters", Trade::MeshData3D(Mesh::Primitive::Lines, {}, {}, {}, {}));
Implementation::WireframeSpheroid cylinder(segments/4); Implementation::WireframeSpheroid cylinder(segments/4);
const Float increment = 2*halfLength/rings;
/* Rings */ /* Rings */
cylinder.ring(-length/2); cylinder.ring(-halfLength);
for(UnsignedInt i = 0; i != rings; ++i) { for(UnsignedInt i = 0; i != rings; ++i) {
cylinder.cylinder(); cylinder.cylinder();
cylinder.ring(-length/2 + (i+1)*(length/rings)); cylinder.ring(-halfLength + (i+1)*increment);
} }
return cylinder.finalize(); return cylinder.finalize();

8
src/Primitives/Cylinder.h

@ -62,7 +62,7 @@ class MAGNUM_PRIMITIVES_EXPORT Cylinder {
* equal to 1. * equal to 1.
* @param segments Number of (face) segments. Must be larger or * @param segments Number of (face) segments. Must be larger or
* equal to 3. * equal to 3.
* @param length Cylinder length * @param halfLength Half the cylinder length
* @param flags Flags * @param flags Flags
* *
* Indexed @ref Mesh::Primitive "Triangles" with normals, optional 2D * Indexed @ref Mesh::Primitive "Triangles" with normals, optional 2D
@ -70,7 +70,7 @@ class MAGNUM_PRIMITIVES_EXPORT Cylinder {
* are generated, vertices of one segment are duplicated for texture * are generated, vertices of one segment are duplicated for texture
* wrapping. * wrapping.
*/ */
static Trade::MeshData3D solid(UnsignedInt rings, UnsignedInt segments, Float length, Flags flags = Flags()); static Trade::MeshData3D solid(UnsignedInt rings, UnsignedInt segments, Float halfLength, Flags flags = Flags());
/** /**
* @brief Wireframe cylinder * @brief Wireframe cylinder
@ -78,11 +78,11 @@ class MAGNUM_PRIMITIVES_EXPORT Cylinder {
* to 1. * to 1.
* @param segments Number of (line) segments. Must be larger or * @param segments Number of (line) segments. Must be larger or
* equal to 4 and multiple of 4. * equal to 4 and multiple of 4.
* @param length Cylinder length * @param halfLength Half the cylinder length
* *
* Indexed @ref Mesh::Primitive "Lines". * Indexed @ref Mesh::Primitive "Lines".
*/ */
static Trade::MeshData3D wireframe(UnsignedInt rings, UnsignedInt segments, Float length); static Trade::MeshData3D wireframe(UnsignedInt rings, UnsignedInt segments, Float halfLength);
}; };
CORRADE_ENUMSET_OPERATORS(Cylinder::Flags) CORRADE_ENUMSET_OPERATORS(Cylinder::Flags)

92
src/Primitives/Test/CapsuleTest.cpp

@ -29,6 +29,7 @@
#include <TestSuite/Compare/Container.h> #include <TestSuite/Compare/Container.h>
#include "Math/Vector3.h" #include "Math/Vector3.h"
#include "Trade/MeshData2D.h"
#include "Trade/MeshData3D.h" #include "Trade/MeshData3D.h"
#include "Primitives/Capsule.h" #include "Primitives/Capsule.h"
@ -38,19 +39,66 @@ class CapsuleTest: public TestSuite::Tester {
public: public:
CapsuleTest(); CapsuleTest();
void solidWithoutTextureCoords(); void wireframe2D();
void solidWithTextureCoords();
void wireframe(); void solid3DWithoutTextureCoords();
void solid3DWithTextureCoords();
void wireframe3D();
}; };
CapsuleTest::CapsuleTest() { CapsuleTest::CapsuleTest() {
addTests({&CapsuleTest::solidWithoutTextureCoords, addTests({&CapsuleTest::wireframe2D,
&CapsuleTest::solidWithTextureCoords, &CapsuleTest::solid3DWithoutTextureCoords,
&CapsuleTest::wireframe}); &CapsuleTest::solid3DWithTextureCoords,
&CapsuleTest::wireframe3D});
} }
void CapsuleTest::solidWithoutTextureCoords() { void CapsuleTest::wireframe2D() {
Trade::MeshData3D capsule = Capsule::solid(2, 2, 3, 1.0f); Trade::MeshData2D capsule = Capsule2D::wireframe(2, 4, 0.5f);
CORRADE_COMPARE_AS(capsule.positions(0), (std::vector<Vector2>{
{0.0f, -1.5f},
{-0.707107f, -1.20711f},
{0.707107f, -1.20711f},
{-1.0f, -0.5f},
{1.0f, -0.5f},
{-1.0f, -0.25f},
{1.0f, -0.25f},
{-1.0f, 0.0f},
{1.0f, 0.0f},
{-1.0f, 0.25f},
{1.0f, 0.25f},
{-1.0f, 0.5f},
{1.0f, 0.5f},
{-0.707107f, 1.20711f},
{0.707107f, 1.20711f},
{0.0f, 1.5f}
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(capsule.indices(), (std::vector<UnsignedInt>{
0, 1, 0, 2,
1, 3, 2, 4,
3, 5, 4, 6,
5, 7, 6, 8,
7, 9, 8, 10,
9, 11, 10, 12,
11, 13, 12, 14,
13, 15, 14, 15
}), TestSuite::Compare::Container);
}
void CapsuleTest::solid3DWithoutTextureCoords() {
Trade::MeshData3D capsule = Capsule3D::solid(2, 4, 3, 0.5f);
CORRADE_COMPARE_AS(capsule.positions(0), (std::vector<Vector3>{ CORRADE_COMPARE_AS(capsule.positions(0), (std::vector<Vector3>{
{0.0f, -1.5f, 0.0f}, {0.0f, -1.5f, 0.0f},
@ -63,10 +111,18 @@ void CapsuleTest::solidWithoutTextureCoords() {
{0.866025f, -0.5f, -0.5f}, {0.866025f, -0.5f, -0.5f},
{-0.866025f, -0.5f, -0.5f}, {-0.866025f, -0.5f, -0.5f},
{0.0f, -0.25f, 1.0f},
{0.866025f, -0.25f, -0.5f},
{-0.866025f, -0.25f, -0.5f},
{0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 1.0f},
{0.866025f, 0.0f, -0.5f}, {0.866025f, 0.0f, -0.5f},
{-0.866025f, 0.0f, -0.5f}, {-0.866025f, 0.0f, -0.5f},
{0.0f, 0.25f, 1.0f},
{0.866025f, 0.25f, -0.5f},
{-0.866025f, 0.25f, -0.5f},
{0.0f, 0.5f, 1.0f}, {0.0f, 0.5f, 1.0f},
{0.866025f, 0.5f, -0.5f}, {0.866025f, 0.5f, -0.5f},
{-0.866025f, 0.5f, -0.5f}, {-0.866025f, 0.5f, -0.5f},
@ -97,6 +153,14 @@ void CapsuleTest::solidWithoutTextureCoords() {
{0.866025f, 0.0f, -0.5f}, {0.866025f, 0.0f, -0.5f},
{-0.866025f, 0.0f, -0.5f}, {-0.866025f, 0.0f, -0.5f},
{0.0f, 0.0f, 1.0f},
{0.866025f, 0.0f, -0.5f},
{-0.866025f, 0.0f, -0.5f},
{0.0f, 0.0f, 1.0f},
{0.866025f, 0.0f, -0.5f},
{-0.866025f, 0.0f, -0.5f},
{0.0f, 0.707107f, 0.707107f}, {0.0f, 0.707107f, 0.707107f},
{0.612372f, 0.707107f, -0.353553f}, {0.612372f, 0.707107f, -0.353553f},
{-0.612372f, 0.707107f, -0.353553f}, {-0.612372f, 0.707107f, -0.353553f},
@ -110,12 +174,14 @@ void CapsuleTest::solidWithoutTextureCoords() {
4, 5, 8, 4, 8, 7, 5, 6, 9, 5, 9, 8, 6, 4, 7, 6, 7, 9, 4, 5, 8, 4, 8, 7, 5, 6, 9, 5, 9, 8, 6, 4, 7, 6, 7, 9,
7, 8, 11, 7, 11, 10, 8, 9, 12, 8, 12, 11, 9, 7, 10, 9, 10, 12, 7, 8, 11, 7, 11, 10, 8, 9, 12, 8, 12, 11, 9, 7, 10, 9, 10, 12,
10, 11, 14, 10, 14, 13, 11, 12, 15, 11, 15, 14, 12, 10, 13, 12, 13, 15, 10, 11, 14, 10, 14, 13, 11, 12, 15, 11, 15, 14, 12, 10, 13, 12, 13, 15,
13, 14, 16, 14, 15, 16, 15, 13, 16 13, 14, 17, 13, 17, 16, 14, 15, 18, 14, 18, 17, 15, 13, 16, 15, 16, 18,
16, 17, 20, 16, 20, 19, 17, 18, 21, 17, 21, 20, 18, 16, 19, 18, 19, 21,
19, 20, 22, 20, 21, 22, 21, 19, 22
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
} }
void CapsuleTest::solidWithTextureCoords() { void CapsuleTest::solid3DWithTextureCoords() {
Trade::MeshData3D capsule = Capsule::solid(2, 2, 3, 1.0f, Capsule::TextureCoords::Generate); Trade::MeshData3D capsule = Capsule3D::solid(2, 2, 3, 0.5f, Capsule3D::TextureCoords::Generate);
CORRADE_COMPARE_AS(capsule.positions(0), (std::vector<Vector3>{ CORRADE_COMPARE_AS(capsule.positions(0), (std::vector<Vector3>{
{0.0f, -1.5f, 0.0f}, {0.0f, -1.5f, 0.0f},
@ -189,8 +255,8 @@ void CapsuleTest::solidWithTextureCoords() {
}), TestSuite::Compare::Container); }), TestSuite::Compare::Container);
} }
void CapsuleTest::wireframe() { void CapsuleTest::wireframe3D() {
Trade::MeshData3D capsule = Capsule::wireframe(2, 2, 8, 1.0f); Trade::MeshData3D capsule = Capsule3D::wireframe(2, 2, 8, 0.5f);
CORRADE_COMPARE_AS(capsule.positions(0), (std::vector<Vector3>{ CORRADE_COMPARE_AS(capsule.positions(0), (std::vector<Vector3>{
{0.0f, -1.5f, 0.0f}, {0.0f, -1.5f, 0.0f},

6
src/Primitives/Test/CylinderTest.cpp

@ -47,7 +47,7 @@ CylinderTest::CylinderTest() {
} }
void CylinderTest::solidWithoutAnything() { void CylinderTest::solidWithoutAnything() {
Trade::MeshData3D cylinder = Cylinder::solid(2, 3, 3.0f); Trade::MeshData3D cylinder = Cylinder::solid(2, 3, 1.5f);
CORRADE_COMPARE_AS(cylinder.positions(0), (std::vector<Vector3>{ CORRADE_COMPARE_AS(cylinder.positions(0), (std::vector<Vector3>{
{0.0f, -1.5f, 1.0f}, {0.0f, -1.5f, 1.0f},
@ -84,7 +84,7 @@ void CylinderTest::solidWithoutAnything() {
} }
void CylinderTest::solidWithTextureCoordsAndCaps() { void CylinderTest::solidWithTextureCoordsAndCaps() {
Trade::MeshData3D cylinder = Cylinder::solid(2, 3, 3.0f, Cylinder::Flag::GenerateTextureCoords|Cylinder::Flag::CapEnds); Trade::MeshData3D cylinder = Cylinder::solid(2, 3, 1.5f, Cylinder::Flag::GenerateTextureCoords|Cylinder::Flag::CapEnds);
CORRADE_COMPARE_AS(cylinder.positions(0), (std::vector<Vector3>{ CORRADE_COMPARE_AS(cylinder.positions(0), (std::vector<Vector3>{
{0.0f, -1.5f, 0.0f}, {0.0f, -1.5f, 0.0f},
@ -188,7 +188,7 @@ void CylinderTest::solidWithTextureCoordsAndCaps() {
} }
void CylinderTest::wireframe() { void CylinderTest::wireframe() {
Trade::MeshData3D cylinder = Cylinder::wireframe(2, 8, 1.0f); Trade::MeshData3D cylinder = Cylinder::wireframe(2, 8, 0.5f);
CORRADE_COMPARE_AS(cylinder.positions(0), (std::vector<Vector3>{ CORRADE_COMPARE_AS(cylinder.positions(0), (std::vector<Vector3>{
{0.0f, -0.5f, 1.0f}, {0.0f, -0.5f, 1.0f},

5
src/Renderer.h

@ -24,7 +24,7 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
/** @file /** @file /Renderer.h
* @brief Class Magnum::Renderer * @brief Class Magnum::Renderer
*/ */
@ -37,9 +37,8 @@
namespace Magnum { namespace Magnum {
/** @nosubgrouping /** @nosubgrouping
@brief %Renderer @brief Global renderer configuration.
Access to global renderer configuration.
@todo @extension{ARB,viewport_array} @todo @extension{ARB,viewport_array}
*/ */
class MAGNUM_EXPORT Renderer { class MAGNUM_EXPORT Renderer {

17
src/ResourceManager.h

@ -313,17 +313,27 @@ template<class... Types> class ResourceManager: private Implementation::Resource
return *this; return *this;
} }
/** @overload */
template<class U> ResourceManager<Types...>& set(ResourceKey key, U&& data, ResourceDataState state, ResourcePolicy policy) {
return set(key, new typename std::remove_cv<typename std::remove_reference<U>::type>::type(std::forward<U>(data)), state, policy);
}
/** /**
* @brief Set resource data * @brief Set resource data
* @return Reference to self (for method chaining) * @return Reference to self (for method chaining)
* *
* Same as above function with state set to @ref ResourceDataState "ResourceDataState::Final" * Same as above with state set to @ref ResourceDataState "ResourceDataState::Final"
* and policy to @ref ResourcePolicy "ResourcePolicy::Resident". * and policy to @ref ResourcePolicy "ResourcePolicy::Resident".
*/ */
template<class T> ResourceManager<Types...>& set(ResourceKey key, T* data) { template<class T> ResourceManager<Types...>& set(ResourceKey key, T* data) {
return set(key, data, ResourceDataState::Final, ResourcePolicy::Resident); return set(key, data, ResourceDataState::Final, ResourcePolicy::Resident);
} }
/** @overload */
template<class U> ResourceManager<Types...>& set(ResourceKey key, U&& data) {
return set(key, new typename std::remove_cv<typename std::remove_reference<U>::type>::type(std::forward<U>(data)));
}
/** @brief Fallback for not found resources */ /** @brief Fallback for not found resources */
template<class T> T* fallback() { template<class T> T* fallback() {
return this->Implementation::ResourceManagerData<T>::fallback(); return this->Implementation::ResourceManagerData<T>::fallback();
@ -343,6 +353,11 @@ template<class... Types> class ResourceManager: private Implementation::Resource
return *this; return *this;
} }
/** @overload */
template<class U> ResourceManager<Types...>& setFallback(U&& data) {
return setFallback(new typename std::remove_cv<typename std::remove_reference<U>::type>::type(std::forward<U>(data)));
}
/** /**
* @brief Free all resources of given type which are not referenced * @brief Free all resources of given type which are not referenced
* @return Reference to self (for method chaining) * @return Reference to self (for method chaining)

6
src/SceneGraph/CMakeLists.txt

@ -92,5 +92,11 @@ if(BUILD_TESTS)
set_target_properties(MagnumSceneGraphTestLib PROPERTIES COMPILE_FLAGS "-DCORRADE_GRACEFUL_ASSERT -DMagnumSceneGraph_EXPORTS") set_target_properties(MagnumSceneGraphTestLib PROPERTIES COMPILE_FLAGS "-DCORRADE_GRACEFUL_ASSERT -DMagnumSceneGraph_EXPORTS")
target_link_libraries(MagnumSceneGraphTestLib MagnumMathTestLib) target_link_libraries(MagnumSceneGraphTestLib MagnumMathTestLib)
# 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 MagnumSceneGraphTestLib DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR})
endif()
add_subdirectory(Test) add_subdirectory(Test)
endif() endif()

2
src/Shapes/CMakeLists.txt

@ -27,6 +27,7 @@ set(MagnumShapes_SRCS
AxisAlignedBox.cpp AxisAlignedBox.cpp
Box.cpp Box.cpp
Capsule.cpp Capsule.cpp
Cylinder.cpp
Composition.cpp Composition.cpp
Line.cpp Line.cpp
Plane.cpp Plane.cpp
@ -44,6 +45,7 @@ set(MagnumShapes_HEADERS
AxisAlignedBox.h AxisAlignedBox.h
Box.h Box.h
Capsule.h Capsule.h
Cylinder.h
Composition.h Composition.h
Line.h Line.h
LineSegment.h LineSegment.h

3
src/Shapes/Capsule.cpp

@ -37,8 +37,7 @@ using namespace Magnum::Math::Geometry;
namespace Magnum { namespace Shapes { namespace Magnum { namespace Shapes {
template<UnsignedInt dimensions> Capsule<dimensions> Capsule<dimensions>::transformed(const typename DimensionTraits<dimensions, Float>::MatrixType& matrix) const { template<UnsignedInt dimensions> Capsule<dimensions> Capsule<dimensions>::transformed(const typename DimensionTraits<dimensions, Float>::MatrixType& matrix) const {
return Capsule<dimensions>(matrix.transformPoint(_a), matrix.transformPoint(_b), return Capsule<dimensions>(matrix.transformPoint(_a), matrix.transformPoint(_b), matrix.uniformScaling()*_radius);
(matrix.rotationScaling()*typename DimensionTraits<dimensions, Float>::VectorType(1/Constants::sqrt3())).length()*_radius);
} }
template<UnsignedInt dimensions> bool Capsule<dimensions>::operator%(const Point<dimensions>& other) const { template<UnsignedInt dimensions> bool Capsule<dimensions>::operator%(const Point<dimensions>& other) const {

10
src/Shapes/Capsule.h

@ -38,11 +38,11 @@ namespace Magnum { namespace Shapes {
/** /**
@brief %Capsule defined by cylinder start and end point and radius @brief %Capsule defined by cylinder start and end point and radius
Unlike other elements the capsule doesn't support asymmetric scaling. When Unlike other elements the capsule expects uniform scaling. See @ref shapes for
applying transformation, the scale factor is averaged from all axes. See brief introduction.
@ref shapes for brief introduction. @see Capsule2D, Capsule3D, Cylinder
@see Capsule2D, Capsule3D @todo Store the radius as squared value to avoid sqrt/pow? Will complicate
@todo Assert for asymmetric scaling collision detection with sphere.
*/ */
template<UnsignedInt dimensions> class MAGNUM_SHAPES_EXPORT Capsule { template<UnsignedInt dimensions> class MAGNUM_SHAPES_EXPORT Capsule {
public: public:

1
src/Shapes/Composition.h

@ -79,6 +79,7 @@ template<UnsignedInt dimensions> class MAGNUM_SHAPES_EXPORT Composition {
Line, /**< Line */ Line, /**< Line */
LineSegment, /**< @ref LineSegment "Line segment" */ LineSegment, /**< @ref LineSegment "Line segment" */
Sphere, /**< Sphere */ Sphere, /**< Sphere */
Cylinder, /**< @ref Cylinder */
Capsule, /**< Capsule */ Capsule, /**< Capsule */
AxisAlignedBox, /**< @ref AxisAlignedBox "Axis aligned box" */ AxisAlignedBox, /**< @ref AxisAlignedBox "Axis aligned box" */
Box, /**< Box */ Box, /**< Box */

58
src/Shapes/Cylinder.cpp

@ -0,0 +1,58 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 "Cylinder.h"
#include "Math/Functions.h"
#include "Math/Matrix3.h"
#include "Math/Matrix4.h"
#include "Math/Geometry/Distance.h"
#include "Magnum.h"
#include "Shapes/Point.h"
#include "Shapes/Sphere.h"
using namespace Magnum::Math::Geometry;
namespace Magnum { namespace Shapes {
template<UnsignedInt dimensions> Cylinder<dimensions> Cylinder<dimensions>::transformed(const typename DimensionTraits<dimensions, Float>::MatrixType& matrix) const {
return Cylinder<dimensions>(matrix.transformPoint(_a), matrix.transformPoint(_b), matrix.uniformScaling()*_radius);
}
template<UnsignedInt dimensions> bool Cylinder<dimensions>::operator%(const Point<dimensions>& other) const {
return Distance::linePointSquared(_a, _b, other.position()) <
Math::pow<2>(_radius);
}
template<UnsignedInt dimensions> bool Cylinder<dimensions>::operator%(const Sphere<dimensions>& other) const {
return Distance::linePointSquared(_a, _b, other.position()) <
Math::pow<2>(_radius+other.radius());
}
#ifndef DOXYGEN_GENERATING_OUTPUT
template class MAGNUM_SHAPES_EXPORT Cylinder<2>;
template class MAGNUM_SHAPES_EXPORT Cylinder<3>;
#endif
}}

117
src/Shapes/Cylinder.h

@ -0,0 +1,117 @@
#ifndef Magnum_Shapes_Cylinder_h
#define Magnum_Shapes_Cylinder_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 Magnum::Shapes::Cylinder, typedef Magnum::Shapes::Cylinder2D, Magnum::Shapes::Cylinder3D
*/
#include "Math/Vector3.h"
#include "DimensionTraits.h"
#include "Shapes/Shapes.h"
#include "Shapes/magnumShapesVisibility.h"
namespace Magnum { namespace Shapes {
/**
@brief Infinite cylinder defined by line and radius
Unlike other elements the cylinder expects uniform scaling. See @ref shapes for
brief introduction.
@see @ref Cylinder2D, @ref Cylinder3D, @ref Capsule
@todo Store the radius as squared value to avoid sqrt/pow? Will complicate
collision detection with sphere.
*/
template<UnsignedInt dimensions> class MAGNUM_SHAPES_EXPORT Cylinder {
public:
enum: UnsignedInt {
Dimensions = dimensions /**< Dimension count */
};
/**
* @brief Constructor
*
* Creates zero-sized cylinder at origin.
*/
constexpr /*implicit*/ Cylinder(): _radius(0.0f) {}
/** @brief Constructor */
constexpr /*implicit*/ Cylinder(const typename DimensionTraits<dimensions, Float>::VectorType& a, const typename DimensionTraits<dimensions, Float>::VectorType& b, Float radius): _a(a), _b(b), _radius(radius) {}
/** @brief Transformed shape */
Cylinder<dimensions> transformed(const typename DimensionTraits<dimensions, Float>::MatrixType& matrix) const;
/** @brief First point */
constexpr typename DimensionTraits<dimensions, Float>::VectorType a() const {
return _a;
}
/** @brief Set first point */
void setA(const typename DimensionTraits<dimensions, Float>::VectorType& a) {
_a = a;
}
/** @brief Second point */
constexpr typename DimensionTraits<dimensions, Float>::VectorType b() const {
return _b;
}
/** @brief Set second point */
void setB(const typename DimensionTraits<dimensions, Float>::VectorType& b) {
_b = b;
}
/** @brief Radius */
constexpr Float radius() const { return _radius; }
/** @brief Set radius */
void setRadius(Float radius) { _radius = radius; }
/** @brief Collision with point */
bool operator%(const Point<dimensions>& other) const;
/** @brief Collision with sphere */
bool operator%(const Sphere<dimensions>& other) const;
private:
typename DimensionTraits<dimensions, Float>::VectorType _a, _b;
Float _radius;
};
/** @brief Infinite two-dimensional cylinder */
typedef Cylinder<2> Cylinder2D;
/** @brief Infinite three-dimensional cylinder */
typedef Cylinder<3> Cylinder3D;
/** @collisionoperator{Point,Cylinder} */
template<UnsignedInt dimensions> inline bool operator%(const Point<dimensions>& a, const Cylinder<dimensions>& b) { return b % a; }
/** @collisionoperator{Sphere,Cylinder} */
template<UnsignedInt dimensions> inline bool operator%(const Sphere<dimensions>& a, const Cylinder<dimensions>& b) { return b % a; }
}}
#endif

7
src/Shapes/Implementation/CollisionDispatch.cpp

@ -27,6 +27,7 @@
#include "Shapes/AxisAlignedBox.h" #include "Shapes/AxisAlignedBox.h"
#include "Shapes/Box.h" #include "Shapes/Box.h"
#include "Shapes/Capsule.h" #include "Shapes/Capsule.h"
#include "Shapes/Cylinder.h"
#include "Shapes/LineSegment.h" #include "Shapes/LineSegment.h"
#include "Shapes/Plane.h" #include "Shapes/Plane.h"
#include "Shapes/Point.h" #include "Shapes/Point.h"
@ -48,6 +49,9 @@ template<> bool collides(const AbstractShape<2>& a, const AbstractShape<2>& b) {
_c(Sphere, Sphere2D, LineSegment, LineSegment2D) _c(Sphere, Sphere2D, LineSegment, LineSegment2D)
_c(Sphere, Sphere2D, Sphere, Sphere2D) _c(Sphere, Sphere2D, Sphere, Sphere2D)
_c(Cylinder, Cylinder2D, Point, Point2D)
_c(Cylinder, Cylinder2D, Sphere, Sphere2D)
_c(Capsule, Capsule2D, Point, Point2D) _c(Capsule, Capsule2D, Point, Point2D)
_c(Capsule, Capsule2D, Sphere, Sphere2D) _c(Capsule, Capsule2D, Sphere, Sphere2D)
@ -71,6 +75,9 @@ template<> bool collides(const AbstractShape<3>& a, const AbstractShape<3>& b) {
_c(Sphere, Sphere3D, LineSegment, LineSegment3D) _c(Sphere, Sphere3D, LineSegment, LineSegment3D)
_c(Sphere, Sphere3D, Sphere, Sphere3D) _c(Sphere, Sphere3D, Sphere, Sphere3D)
_c(Cylinder, Cylinder3D, Point, Point3D)
_c(Cylinder, Cylinder3D, Sphere, Sphere3D)
_c(Capsule, Capsule3D, Point, Point3D) _c(Capsule, Capsule3D, Point, Point3D)
_c(Capsule, Capsule3D, Sphere, Sphere3D) _c(Capsule, Capsule3D, Sphere, Sphere3D)

5
src/Shapes/Plane.cpp

@ -35,8 +35,11 @@ using namespace Magnum::Math::Geometry;
namespace Magnum { namespace Shapes { namespace Magnum { namespace Shapes {
Plane Plane::transformed(const Matrix4& matrix) const { Plane Plane::transformed(const Matrix4& matrix) const {
/* Using matrix.rotation() would result in two more normalizations (slow),
using .normalized() instead of matrix.uniformScaling() would not check
uniform scaling */
return Plane(matrix.transformPoint(_position), return Plane(matrix.transformPoint(_position),
matrix.rotation()*_normal); matrix.rotationScaling()*_normal/matrix.uniformScaling());
} }
bool Plane::operator%(const Line3D& other) const { bool Plane::operator%(const Line3D& other) const {

3
src/Shapes/Plane.h

@ -38,7 +38,8 @@ namespace Magnum { namespace Shapes {
/** /**
@brief Infinite plane, defined by position and normal (3D only) @brief Infinite plane, defined by position and normal (3D only)
See @ref shapes for brief introduction. Unlike other elements the plane expects uniform scaling. See @ref shapes for
brief introduction.
*/ */
class MAGNUM_SHAPES_EXPORT Plane { class MAGNUM_SHAPES_EXPORT Plane {
public: public:

4
src/Shapes/Shapes.h

@ -54,6 +54,10 @@ template<UnsignedInt> class Composition;
typedef Composition<2> Composition2D; typedef Composition<2> Composition2D;
typedef Composition<3> Composition3D; typedef Composition<3> Composition3D;
template<UnsignedInt> class Cylinder;
typedef Cylinder<2> Cylinder2D;
typedef Cylinder<3> Cylinder3D;
template<UnsignedInt> class Line; template<UnsignedInt> class Line;
typedef Line<2> Line2D; typedef Line<2> Line2D;
typedef Line<3> Line3D; typedef Line<3> Line3D;

15
src/Shapes/Sphere.cpp

@ -36,21 +36,8 @@ using namespace Magnum::Math::Geometry;
namespace Magnum { namespace Shapes { namespace Magnum { namespace Shapes {
namespace {
template<UnsignedInt dimensions> static typename DimensionTraits<dimensions, Float>::VectorType unitVector();
template<> inline Vector2 unitVector<2>() {
return Vector2(1/Constants::sqrt2());
}
template<> inline Vector3 unitVector<3>() {
return Vector3(1/Constants::sqrt3());
}
}
template<UnsignedInt dimensions> Sphere<dimensions> Sphere<dimensions>::transformed(const typename DimensionTraits<dimensions, Float>::MatrixType& matrix) const { template<UnsignedInt dimensions> Sphere<dimensions> Sphere<dimensions>::transformed(const typename DimensionTraits<dimensions, Float>::MatrixType& matrix) const {
return Sphere<dimensions>(matrix.transformPoint(_position), return Sphere<dimensions>(matrix.transformPoint(_position), matrix.uniformScaling()*_radius);
(matrix.rotationScaling()*unitVector<dimensions>()).length()*_radius);
} }
template<UnsignedInt dimensions> bool Sphere<dimensions>::operator%(const Point<dimensions>& other) const { template<UnsignedInt dimensions> bool Sphere<dimensions>::operator%(const Point<dimensions>& other) const {

8
src/Shapes/Sphere.h

@ -38,11 +38,11 @@ namespace Magnum { namespace Shapes {
/** /**
@brief %Sphere defined by position and radius @brief %Sphere defined by position and radius
Unlike other elements the sphere doesn't support asymmetric scaling. When Unlike other elements the sphere expects uniform scaling. See @ref shapes for
applying transformation, the scale factor is averaged from all axes. See brief introduction.
@ref shapes for brief introduction.
@see Sphere2D, Sphere3D @see Sphere2D, Sphere3D
@todo Assert for asymmetric scaling @todo Store the radius as squared value to avoid sqrt/pow? Will complicate
collision detection with another sphere.
*/ */
template<UnsignedInt dimensions> class MAGNUM_SHAPES_EXPORT Sphere { template<UnsignedInt dimensions> class MAGNUM_SHAPES_EXPORT Sphere {
public: public:

1
src/Shapes/Test/CMakeLists.txt

@ -26,6 +26,7 @@ corrade_add_test(ShapesShapeImplementationTest ShapeImplementationTest.cpp LIBRA
corrade_add_test(ShapesAxisAlignedBoxTest AxisAlignedBoxTest.cpp LIBRARIES MagnumShapes) corrade_add_test(ShapesAxisAlignedBoxTest AxisAlignedBoxTest.cpp LIBRARIES MagnumShapes)
corrade_add_test(ShapesBoxTest BoxTest.cpp LIBRARIES MagnumShapes) corrade_add_test(ShapesBoxTest BoxTest.cpp LIBRARIES MagnumShapes)
corrade_add_test(ShapesCapsuleTest CapsuleTest.cpp LIBRARIES MagnumShapes) corrade_add_test(ShapesCapsuleTest CapsuleTest.cpp LIBRARIES MagnumShapes)
corrade_add_test(ShapesCylinderTest CylinderTest.cpp LIBRARIES MagnumShapes)
corrade_add_test(ShapesLineTest LineTest.cpp LIBRARIES MagnumShapes) corrade_add_test(ShapesLineTest LineTest.cpp LIBRARIES MagnumShapes)
corrade_add_test(ShapesPlaneTest PlaneTest.cpp LIBRARIES MagnumShapes) corrade_add_test(ShapesPlaneTest PlaneTest.cpp LIBRARIES MagnumShapes)
corrade_add_test(ShapesPointTest PointTest.cpp LIBRARIES MagnumShapes) corrade_add_test(ShapesPointTest PointTest.cpp LIBRARIES MagnumShapes)

13
src/Shapes/Test/CapsuleTest.cpp

@ -22,6 +22,7 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include "Math/Matrix3.h"
#include "Math/Matrix4.h" #include "Math/Matrix4.h"
#include "Magnum.h" #include "Magnum.h"
#include "Shapes/Capsule.h" #include "Shapes/Capsule.h"
@ -51,14 +52,10 @@ CapsuleTest::CapsuleTest() {
void CapsuleTest::transformed() { void CapsuleTest::transformed() {
const Shapes::Capsule3D capsule({1.0f, 2.0f, 3.0f}, {-1.0f, -2.0f, -3.0f}, 7.0f); const Shapes::Capsule3D capsule({1.0f, 2.0f, 3.0f}, {-1.0f, -2.0f, -3.0f}, 7.0f);
const auto transformed = capsule.transformed(Matrix4::rotation(Deg(90.0f), Vector3::zAxis())); const auto transformed = capsule.transformed(Matrix4::scaling(Vector3(2.0f))*Matrix4::rotation(Deg(90.0f), Vector3::zAxis()));
CORRADE_COMPARE(transformed.a(), Vector3(-2.0f, 1.0f, 3.0f)); CORRADE_COMPARE(transformed.a(), Vector3(-4.0f, 2.0f, 6.0f));
CORRADE_COMPARE(transformed.b(), Vector3(2.0f, -1.0f, -3.0f)); CORRADE_COMPARE(transformed.b(), Vector3(4.0f, -2.0f, -6.0f));
CORRADE_COMPARE(transformed.radius(), 7.0f); CORRADE_COMPARE(transformed.radius(), 14.0f);
/* Apply average scaling to radius */
const auto scaled = capsule.transformed(Matrix4::scaling({Constants::sqrt3(), -Constants::sqrt2(), 2.0f}));
CORRADE_COMPARE(scaled.radius(), Constants::sqrt3()*7.0f);
} }
void CapsuleTest::collisionPoint() { void CapsuleTest::collisionPoint() {

85
src/Shapes/Test/CylinderTest.cpp

@ -0,0 +1,85 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 "Math/Matrix3.h"
#include "Math/Matrix4.h"
#include "Magnum.h"
#include "Shapes/Cylinder.h"
#include "Shapes/Point.h"
#include "Shapes/Sphere.h"
#include "ShapeTestBase.h"
namespace Magnum { namespace Shapes { namespace Test {
class CylinderTest: public TestSuite::Tester {
public:
CylinderTest();
void transformed();
void transformedAverageScaling();
void collisionPoint();
void collisionSphere();
};
CylinderTest::CylinderTest() {
addTests({&CylinderTest::transformed,
&CylinderTest::collisionPoint,
&CylinderTest::collisionSphere});
}
void CylinderTest::transformed() {
const Shapes::Cylinder3D cylinder({1.0f, 2.0f, 3.0f}, {-1.0f, -2.0f, -3.0f}, 7.0f);
const auto transformed = cylinder.transformed(Matrix4::scaling(Vector3(2.0f))*Matrix4::rotation(Deg(90.0f), Vector3::zAxis()));
CORRADE_COMPARE(transformed.a(), Vector3(-4.0f, 2.0f, 6.0f));
CORRADE_COMPARE(transformed.b(), Vector3(4.0f, -2.0f, -6.0f));
CORRADE_COMPARE(transformed.radius(), 14.0f);
}
void CylinderTest::collisionPoint() {
Shapes::Cylinder3D cylinder({-1.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, 2.0f);
Shapes::Point3D point({2.0f, 0.0f, 0.0f});
Shapes::Point3D point1({1.0f, 3.1f, 0.0f});
Shapes::Point3D point2({2.9f, -1.0f, 0.0f});
VERIFY_COLLIDES(cylinder, point);
VERIFY_COLLIDES(cylinder, point1);
VERIFY_NOT_COLLIDES(cylinder, point2);
}
void CylinderTest::collisionSphere() {
Shapes::Cylinder3D cylinder({-1.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, 2.0f);
Shapes::Sphere3D sphere({3.0f, 0.0f, 0.0f}, 0.9f);
Shapes::Sphere3D sphere1({1.0f, 4.1f, 0.0f}, 1.0f);
Shapes::Sphere3D sphere2({3.5f, -1.0f, 0.0f}, 0.6f);
VERIFY_COLLIDES(cylinder, sphere);
VERIFY_COLLIDES(cylinder, sphere1);
VERIFY_NOT_COLLIDES(cylinder, sphere2);
}
}}}
CORRADE_TEST_MAIN(Magnum::Shapes::Test::CylinderTest)

10
src/Shapes/Test/PlaneTest.cpp

@ -49,14 +49,10 @@ PlaneTest::PlaneTest() {
void PlaneTest::transformed() { void PlaneTest::transformed() {
const Shapes::Plane plane({1.0f, 2.0f, 3.0f}, {Constants::sqrt2(), -Constants::sqrt2(), 0}); const Shapes::Plane plane({1.0f, 2.0f, 3.0f}, {Constants::sqrt2(), -Constants::sqrt2(), 0});
const auto transformed = plane.transformed(Matrix4::rotation(Deg(90.0f), Vector3::xAxis())); /* The normal should stay normalized after scaling */
CORRADE_COMPARE(transformed.position(), Vector3(1.0f, -3.0f, 2.0f)); const auto transformed = plane.transformed(Matrix4::scaling(Vector3(2.0f))*Matrix4::rotation(Deg(90.0f), Vector3::xAxis()));
CORRADE_COMPARE(transformed.position(), Vector3(2.0f, -6.0f, 4.0f));
CORRADE_COMPARE(transformed.normal(), Vector3(Constants::sqrt2(), 0, -Constants::sqrt2())); CORRADE_COMPARE(transformed.normal(), Vector3(Constants::sqrt2(), 0, -Constants::sqrt2()));
/* The normal should stay normalized */
const auto scaled = plane.transformed(Matrix4::scaling({1.5f, 2.0f, 3.0f}));
CORRADE_COMPARE(scaled.position(), Vector3(1.5f, 4.0f, 9.0f));
CORRADE_COMPARE(scaled.normal(), Vector3(Constants::sqrt2(), -Constants::sqrt2(), 0));
} }
void PlaneTest::collisionLine() { void PlaneTest::collisionLine() {

16
src/Shapes/Test/SphereTest.cpp

@ -22,6 +22,7 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include "Math/Matrix3.h"
#include "Math/Matrix4.h" #include "Math/Matrix4.h"
#include "Magnum.h" #include "Magnum.h"
#include "Shapes/LineSegment.h" #include "Shapes/LineSegment.h"
@ -54,18 +55,9 @@ SphereTest::SphereTest() {
void SphereTest::transformed() { void SphereTest::transformed() {
const Shapes::Sphere3D sphere({1.0f, 2.0f, 3.0f}, 7.0f); const Shapes::Sphere3D sphere({1.0f, 2.0f, 3.0f}, 7.0f);
const auto transformed = sphere.transformed(Matrix4::rotation(Deg(90.0f), Vector3::yAxis())); const auto transformed = sphere.transformed(Matrix4::scaling(Vector3(2.0f))*Matrix4::rotation(Deg(90.0f), Vector3::yAxis()));
CORRADE_COMPARE(transformed.position(), Vector3(3.0f, 2.0f, -1.0f)); CORRADE_COMPARE(transformed.position(), Vector3(6.0f, 4.0f, -2.0f));
CORRADE_COMPARE(transformed.radius(), 7.0f); CORRADE_COMPARE(transformed.radius(), 14.0f);
/* Symmetric scaling */
const auto scaled = sphere.transformed(Matrix4::scaling(Vector3(2.0f)));
CORRADE_COMPARE(scaled.position(), Vector3(2.0f, 4.0f, 6.0f));
CORRADE_COMPARE(scaled.radius(), 14.0f);
/* Apply average scaling to radius */
const auto nonEven = sphere.transformed(Matrix4::scaling({Constants::sqrt3(), -Constants::sqrt2(), 2.0f}));
CORRADE_COMPARE(nonEven.radius(), Constants::sqrt3()*7.0f);
} }
void SphereTest::collisionPoint() { void SphereTest::collisionPoint() {

2
src/Shapes/shapeImplementation.cpp

@ -36,6 +36,7 @@ Debug operator<<(Debug debug, ShapeDimensionTraits<2>::Type value) {
_val(LineSegment) _val(LineSegment)
_val(Sphere) _val(Sphere)
_val(Capsule) _val(Capsule)
_val(Cylinder)
_val(AxisAlignedBox) _val(AxisAlignedBox)
_val(Box) _val(Box)
_val(Composition) _val(Composition)
@ -53,6 +54,7 @@ Debug operator<<(Debug debug, ShapeDimensionTraits<3>::Type value) {
_val(LineSegment) _val(LineSegment)
_val(Sphere) _val(Sphere)
_val(Capsule) _val(Capsule)
_val(Cylinder)
_val(AxisAlignedBox) _val(AxisAlignedBox)
_val(Box) _val(Box)
_val(Plane) _val(Plane)

43
src/Shapes/shapeImplementation.h

@ -26,6 +26,7 @@
#include <utility> #include <utility>
#include <Utility/Assert.h> #include <Utility/Assert.h>
#include <corradeCompatibility.h>
#include "DimensionTraits.h" #include "DimensionTraits.h"
#include "Magnum.h" #include "Magnum.h"
@ -34,6 +35,23 @@
namespace Magnum { namespace Shapes { namespace Implementation { namespace Magnum { namespace Shapes { namespace Implementation {
/*
Adding new collision type:
1. Add the type into the 2D/3D enums below, pick new prime number and
preserve complexity ordering
2. Update debug output operators for changed enums
3. Add TypeOf struct specialization (either for both 2D/3D or for only one
of them)
4. Add the enum value to (documentation-only) enum in Composition
5. Update doc/shapes.dox with new type
Adding new collision detection implementation:
1. Update Implementation/CollisionDispatch.cpp with newly implemented
2D/3D pair
*/
/* Shape type for given dimension count */ /* Shape type for given dimension count */
template<UnsignedInt> struct ShapeDimensionTraits; template<UnsignedInt> struct ShapeDimensionTraits;
@ -44,10 +62,11 @@ template<> struct ShapeDimensionTraits<2> {
Line = 2, Line = 2,
LineSegment = 3, LineSegment = 3,
Sphere = 5, Sphere = 5,
Capsule = 7, Cylinder = 7,
AxisAlignedBox = 11, Capsule = 11,
Box = 13, AxisAlignedBox = 13,
Composition = 17 Box = 17,
Composition = 19
}; };
}; };
@ -57,11 +76,12 @@ template<> struct ShapeDimensionTraits<3> {
Line = 2, Line = 2,
LineSegment = 3, LineSegment = 3,
Sphere = 5, Sphere = 5,
Capsule = 7, Cylinder = 7,
AxisAlignedBox = 11, Capsule = 11,
Box = 13, AxisAlignedBox = 13,
Plane = 17, Box = 17,
Composition = 19 Plane = 19,
Composition = 23
}; };
}; };
@ -92,6 +112,11 @@ template<UnsignedInt dimensions> struct TypeOf<Shapes::Sphere<dimensions>> {
return ShapeDimensionTraits<dimensions>::Type::Sphere; return ShapeDimensionTraits<dimensions>::Type::Sphere;
} }
}; };
template<UnsignedInt dimensions> struct TypeOf<Shapes::Cylinder<dimensions>> {
constexpr static typename ShapeDimensionTraits<dimensions>::Type type() {
return ShapeDimensionTraits<dimensions>::Type::Cylinder;
}
};
template<UnsignedInt dimensions> struct TypeOf<Shapes::Capsule<dimensions>> { template<UnsignedInt dimensions> struct TypeOf<Shapes::Capsule<dimensions>> {
constexpr static typename ShapeDimensionTraits<dimensions>::Type type() { constexpr static typename ShapeDimensionTraits<dimensions>::Type type() {
return ShapeDimensionTraits<dimensions>::Type::Capsule; return ShapeDimensionTraits<dimensions>::Type::Capsule;

1
src/Test/AbstractOpenGLTester.h

@ -25,6 +25,7 @@
*/ */
#include <TestSuite/Tester.h> #include <TestSuite/Tester.h>
#include <corradeCompatibility.h>
#include "Renderer.h" #include "Renderer.h"

21
src/Test/ResourceManagerTest.cpp

@ -98,7 +98,7 @@ void ResourceManagerTest::state() {
void ResourceManagerTest::stateFallback() { void ResourceManagerTest::stateFallback() {
{ {
ResourceManager rm; ResourceManager rm;
rm.setFallback<Data>(new Data); rm.setFallback(new Data);
Resource<Data> data = rm.get<Data>("data"); Resource<Data> data = rm.get<Data>("data");
CORRADE_VERIFY(data); CORRADE_VERIFY(data);
@ -129,8 +129,7 @@ void ResourceManagerTest::stateDisallowed() {
std::ostringstream out; std::ostringstream out;
Error::setOutput(&out); Error::setOutput(&out);
Data d; rm.set("data", Data(), ResourceDataState::Loading, ResourcePolicy::Resident);
rm.set("data", &d, ResourceDataState::Loading, ResourcePolicy::Resident);
CORRADE_COMPARE(out.str(), "ResourceManager::set(): data should be null if and only if state is NotFound or Loading\n"); CORRADE_COMPARE(out.str(), "ResourceManager::set(): data should be null if and only if state is NotFound or Loading\n");
out.str({}); out.str({});
@ -144,8 +143,8 @@ void ResourceManagerTest::basic() {
/* One mutable, one final */ /* One mutable, one final */
ResourceKey questionKey("the-question"); ResourceKey questionKey("the-question");
ResourceKey answerKey("the-answer"); ResourceKey answerKey("the-answer");
rm.set(questionKey, new Int(10), ResourceDataState::Mutable, ResourcePolicy::Resident); rm.set(questionKey, 10, ResourceDataState::Mutable, ResourcePolicy::Resident);
rm.set(answerKey, new Int(42), ResourceDataState::Final, ResourcePolicy::Resident); rm.set(answerKey, 42, ResourceDataState::Final, ResourcePolicy::Resident);
Resource<Int> theQuestion = rm.get<Int>(questionKey); Resource<Int> theQuestion = rm.get<Int>(questionKey);
Resource<Int> theAnswer = rm.get<Int>(answerKey); Resource<Int> theAnswer = rm.get<Int>(answerKey);
@ -159,12 +158,12 @@ void ResourceManagerTest::basic() {
/* Cannot change already final resource */ /* Cannot change already final resource */
std::ostringstream out; std::ostringstream out;
Error::setOutput(&out); Error::setOutput(&out);
rm.set(answerKey, new Int(43), ResourceDataState::Mutable, ResourcePolicy::Resident); rm.set(answerKey, 43, ResourceDataState::Mutable, ResourcePolicy::Resident);
CORRADE_COMPARE(*theAnswer, 42); CORRADE_COMPARE(*theAnswer, 42);
CORRADE_COMPARE(out.str(), "ResourceManager::set(): cannot change already final resource " + answerKey.hexString() + '\n'); CORRADE_COMPARE(out.str(), "ResourceManager::set(): cannot change already final resource " + answerKey.hexString() + '\n');
/* But non-final can be changed */ /* But non-final can be changed */
rm.set(questionKey, new Int(20), ResourceDataState::Final, ResourcePolicy::Resident); rm.set(questionKey, 20, ResourceDataState::Final, ResourcePolicy::Resident);
CORRADE_COMPARE(theQuestion.state(), ResourceState::Final); CORRADE_COMPARE(theQuestion.state(), ResourceState::Final);
CORRADE_COMPARE(*theQuestion, 20); CORRADE_COMPARE(*theQuestion, 20);
} }
@ -189,7 +188,7 @@ void ResourceManagerTest::referenceCountedPolicy() {
/* Reference counted resources must be requested first */ /* Reference counted resources must be requested first */
{ {
rm.set(dataRefCountKey, new Data(), ResourceDataState::Final, ResourcePolicy::ReferenceCounted); rm.set(dataRefCountKey, new Data, ResourceDataState::Final, ResourcePolicy::ReferenceCounted);
CORRADE_COMPARE(rm.count<Data>(), 0); CORRADE_COMPARE(rm.count<Data>(), 0);
Resource<Data> data = rm.get<Data>(dataRefCountKey); Resource<Data> data = rm.get<Data>(dataRefCountKey);
CORRADE_COMPARE(data.state(), ResourceState::NotLoaded); CORRADE_COMPARE(data.state(), ResourceState::NotLoaded);
@ -198,7 +197,7 @@ void ResourceManagerTest::referenceCountedPolicy() {
Resource<Data> data = rm.get<Data>(dataRefCountKey); Resource<Data> data = rm.get<Data>(dataRefCountKey);
CORRADE_COMPARE(rm.count<Data>(), 1); CORRADE_COMPARE(rm.count<Data>(), 1);
CORRADE_COMPARE(data.state(), ResourceState::NotLoaded); CORRADE_COMPARE(data.state(), ResourceState::NotLoaded);
rm.set(dataRefCountKey, new Data(), ResourceDataState::Final, ResourcePolicy::ReferenceCounted); rm.set(dataRefCountKey, new Data, ResourceDataState::Final, ResourcePolicy::ReferenceCounted);
CORRADE_COMPARE(data.state(), ResourceState::Final); CORRADE_COMPARE(data.state(), ResourceState::Final);
CORRADE_COMPARE(Data::count, 1); CORRADE_COMPARE(Data::count, 1);
} }
@ -250,7 +249,7 @@ void ResourceManagerTest::clearWhileReferenced() {
Error::setOutput(&out); Error::setOutput(&out);
ResourceManager rm; ResourceManager rm;
rm.set("blah", new Int); rm.set("blah", Int());
/** @todo this will leak, is there any better solution without hitting /** @todo this will leak, is there any better solution without hitting
assertion in decrementReferenceCount()? */ assertion in decrementReferenceCount()? */
new Resource<Int>(rm.get<Int>("blah")); new Resource<Int>(rm.get<Int>("blah"));
@ -265,7 +264,7 @@ void ResourceManagerTest::loader() {
IntResourceLoader(): resource(ResourceManager::instance().get<Data>("data")) {} IntResourceLoader(): resource(ResourceManager::instance().get<Data>("data")) {}
void load() { void load() {
set("hello", new Int(773), ResourceDataState::Final, ResourcePolicy::Resident); set("hello", 773, ResourceDataState::Final, ResourcePolicy::Resident);
setNotFound("world"); setNotFound("world");
} }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save