diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f66db2e3..09f2138da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,9 +13,11 @@ option(WITH_EVERYTHING "Build everything (doesn't include contexts)" ON) option(WITH_MESHTOOLS "Build MeshTools library" OFF) option(WITH_PHYSICS "Build Physics library" OFF) option(WITH_PRIMITIVES "Builf Primitives library" OFF) +option(WITH_SCENEGRAPH "Build SceneGraph library" OFF) option(WITH_SHADERS "Build Shaders library" OFF) -cmake_dependent_option(WITH_EGLCONTEXT "Build EglContext library" OFF "TARGET_GLES" OFF) +option(WITH_GLXCONTEXT "Build GlxContext library" OFF) +cmake_dependent_option(WITH_XEGLCONTEXT "Build XEglContext library" OFF "TARGET_GLES" OFF) cmake_dependent_option(WITH_GLUTCONTEXT "Build GlutContext library" OFF "NOT TARGET_GLES" OFF) option(WITH_SDL2CONTEXT "Build Sdl2Context library" OFF) @@ -25,6 +27,7 @@ if(WITH_EVERYTHING) set(WITH_MESHTOOLS ON) set(WITH_PHYSICS ON) set(WITH_PRIMITIVES ON) + set(WITH_SCENEGRAPH ON) set(WITH_SHADERS ON) endif() diff --git a/Doxyfile b/Doxyfile index 40f5bcd66..a9b8a684f 100644 --- a/Doxyfile +++ b/Doxyfile @@ -195,7 +195,9 @@ TAB_SIZE = 8 # You can put \n's in the value part of an alias to insert newlines. ALIASES = \ - "debugoperator{1}=@relates \1\n@brief Debug output operator" \ + "debugoperator{1}=@relates \1\n@brief Debug output operator @xrefitem debugoperators \"Debug output operator\" \"Debug output operators for custom types\" Allows printing \1 with Corrade::Utility::Debug and friends." \ + "configurationvalueref{1}=@see @ref configurationvalues \"Corrade::Utility::ConfigurationValue<\1>\"" \ + "configurationvalue{1}=@brief %Configuration value parser and writer @xrefitem configurationvalues \"Configuration value parser and writer\" \"Configuration value parsers and writers for custom types\" Allows parsing and writing \1 from and to Corrade::Utility::Configuration." \ "collisionoperator{2}=@relates \1\n@brief Collision of %\1 and %\2\n@see \2::operator%(const \1&) const" \ "todoc=@xrefitem todoc \"Documentation todo\" \"Documentation-related todo list\"" \ "requires_gl=@xrefitem requires-gl \"Requires desktop OpenGL\" \"Functionality requiring desktop OpenGL (not available on OpenGL ES)\" Not available on OpenGL ES." \ @@ -206,8 +208,12 @@ ALIASES = \ "requires_gl40=@xrefitem requires-gl40 \"Requires OpenGL 4.0\" \"Functionality requiring OpenGL 4.0\"" \ "requires_gl41=@xrefitem requires-gl41 \"Requires OpenGL 4.1\" \"Functionality requiring OpenGL 4.1\"" \ "requires_gl42=@xrefitem requires-gl42 \"Requires OpenGL 4.2\" \"Functionality requiring OpenGL 4.2\"" \ - "requires_extension=@xrefitem requires-extension \"Requires extension\" \"Functionality requiring specific OpenGL extension\"" \ - "extension{2}=\1_\2" + "requires_gl43=@xrefitem requires-gl43 \"Requires OpenGL 4.3\" \"Functionality requiring OpenGL 4.3\"" \ + "requires_extension=@xrefitem requires-extension \"Requires OpenGL extension\" \"Functionality requiring specific OpenGL extension\"" \ + "extension{2}=\1_\2" \ + "requires_gles30=@xrefitem requires-gles30 \"Requires OpenGL ES 3.0\" \"Functionality requiring OpenGL ES 3.0\"" \ + "requires_es_extension=@xrefitem requires-es-extension \"Requires OpenGL ES extension\" \"Functionality requiring specific OpenGL ES extension\"" \ + "es_extension{2}=\1_\2" # 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 diff --git a/doc/building.dox b/doc/building.dox index 5f85874d8..5153bf4b4 100644 --- a/doc/building.dox +++ b/doc/building.dox @@ -15,7 +15,7 @@ Minimal set of tools and libraries required for building is: which are tested to support everything needed: **GCC** >= 4.6 and **Clang** >= 3.1. - **CMake** >= 2.8.8 (needed for `OBJECT` library target) -- **OpenGL** headers, on Linux most probably shipped with Mesa or +- **OpenGL** headers, on Linux most probably shipped with Mesa, or **OpenGL ES 2** headers, if targeting OpenGL ES (see below). - **GLEW** - OpenGL extension wrangler - **Corrade** - Plugin management and utility library. You can get it at diff --git a/doc/coding-style.dox b/doc/coding-style.dox index acab6dc81..09fbdcb80 100644 --- a/doc/coding-style.dox +++ b/doc/coding-style.dox @@ -33,8 +33,8 @@ removing redundant prefixes) is encouraged. @subsection documentation-commands Special documentation commands -Additionally to @c \@todoc and @c \@debugoperator (same as in Corrade), these -are defined: +Additionally to @c \@todoc, @c \@debugoperator @c \@configurationvalue and +@c \@configurationvalueref (same as in Corrade), these are defined: @subsubsection documentation-commands-collisionoperator Physics collision operators diff --git a/doc/groups.dox b/doc/groups.dox deleted file mode 100644 index 22e833e4b..000000000 --- a/doc/groups.dox +++ /dev/null @@ -1,29 +0,0 @@ -namespace Magnum { - -/** @page modules */ - -/** -@defgroup scene Scene graph -@brief %Scene hierarchy, cameras, lights and other objects -*/ - -/** -@defgroup mesh Meshes -@brief Creating, modifying and rendering meshes -*/ - -/** -@defgroup rendering Rendering -@brief %Shader, buffer and mesh management, render queries -*/ - -/** @ingroup rendering -@defgroup textures Textures -@brief Access to OpenGL textures, image data wrappers and framebuffer operations -*/ - -/** -@defgroup utility Utility -@brief Various supporting utility classes -*/ -} diff --git a/doc/mainpage.dox b/doc/mainpage.dox index 3e369b994..093bcf0ce 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -6,8 +6,8 @@ Features: - Easy-to-use templated @ref Math "mathematical library" for matrix/vector calculations and @ref Math::Geometry "geometry". -- Hierarchical @ref Object "scene graph" which supports transformation caching - for better performance, classes for convenient usage of +- Hierarchical @ref SceneGraph "scene graph" which supports transformation + caching for better performance, classes for convenient usage of @ref AbstractShaderProgram "shaders", @ref Buffer "buffers" and @ref AbstractTexture "textures". Access to @ref Framebuffer "framebuffer" and @ref AbstractQuery "occlusion queries". @@ -50,11 +50,6 @@ in step-by-step tutorial. Then you can dig deeper and try @ref example-index "other examples", read about fundamental principles in the documentation or start experimenting on your own! -@subsection module-overview Module overview - -Classes in %Magnum are grouped into a few modules for a better orientation. -Check the @ref modules "module index" for more information about all classes. - @subsection getting-started-hacking Hacking Magnum If you want to hack on this engine, if you spotted a bug, need an feature or diff --git a/doc/namespaces.dox b/doc/namespaces.dox index 1905ab375..63587bca1 100644 --- a/doc/namespaces.dox +++ b/doc/namespaces.dox @@ -7,8 +7,7 @@ /** @namespace Magnum @brief Root namespace -Contains classes needed for building meshes, setting up and rendering the -scene. +Contains classes for interacting with OpenGL. */ /** @dir Contexts @@ -41,8 +40,7 @@ Functions for computing intersections, distances, areas and volumes. /** @dir MeshTools * @brief Namespace Magnum::MeshTools */ -/** @ingroup mesh -@namespace Magnum::MeshTools +/** @namespace Magnum::MeshTools @brief %Mesh tools Tools for generating, optimizing and cleaning meshes. @@ -51,18 +49,26 @@ Tools for generating, optimizing and cleaning meshes. /** @dir Primitives * @brief Namespace Magnum::Primitives */ -/** @ingroup mesh -@namespace Magnum::Primitives +/** @namespace Magnum::Primitives @brief Primitive library Basic primitives for testing purposes. */ +/** @dir SceneGraph + * @brief Namespace Magnum::SceneGraph + */ +/** +@namespace Magnum::SceneGraph +@brief %Scene graph library + +Setting up and rendering the scene. +*/ + /** @dir Shaders * @brief Namespace Magnum::Shaders */ -/** @ingroup rendering -@namespace Magnum::Shaders +/** @namespace Magnum::Shaders @brief Sample shaders Collection of shaders for testing purposes. diff --git a/doc/required-extensions.dox b/doc/required-extensions.dox index c35629ca5..443109d1a 100644 --- a/doc/required-extensions.dox +++ b/doc/required-extensions.dox @@ -20,7 +20,10 @@ supported on Intel GPUs even if they are capable of OpenGL 2.1 only). - @subpage requires-gl40 - @subpage requires-gl41 - @subpage requires-gl42 +- @subpage requires-gl43 - @subpage requires-extension +- @subpage requires-gles30 +- @subpage requires-es-extension @page requires-gl Functionality requiring desktop OpenGL (not available on OpenGL ES) @page requires-gl30 Functionality requiring OpenGL 3.0 @@ -30,6 +33,9 @@ supported on Intel GPUs even if they are capable of OpenGL 2.1 only). @page requires-gl40 Functionality requiring OpenGL 4.0 @page requires-gl41 Functionality requiring OpenGL 4.1 @page requires-gl42 Functionality requiring OpenGL 4.2 +@page requires-gl43 Functionality requiring OpenGL 4.3 @page requires-extension Functionality requiring specific OpenGL extension +@page requires-gles30 Functionality requiring OpenGL ES 3.0 +@page requires-es-extension Functionality requiring specific OpenGL ES extension */ diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake index 09a41585c..fada6ad10 100644 --- a/modules/FindMagnum.cmake +++ b/modules/FindMagnum.cmake @@ -18,8 +18,10 @@ # MeshTools - MeshTools library # Physics - Physics library # Primitives - Library with stock geometric primitives (static) +# SceneGraph - Scene graph library # Shaders - Library with stock shaders -# EglContext - EGL context (depends on EGL and X11 libraries) +# GlxContext - GLX context (depends on X11 libraries) +# XEglContext - X/EGL context (depends on EGL and X11 libraries) # GlutContext - GLUT context (depends on GLUT library) # Sdl2Context - SDL2 context (depends on SDL2 library) # Example usage with specifying additional components is: @@ -110,8 +112,18 @@ foreach(component ${Magnum_FIND_COMPONENTS}) endif() endif() + # GLX context dependencies + if(${component} STREQUAL GlxContext) + find_package(X11) + if(X11_FOUND) + set(_MAGNUM_${_COMPONENT}_LIBRARIES ${X11_LIBRARIES}) + else() + unset(MAGNUM_${_COMPONENT}_LIBRARY) + endif() + endif() + # X/EGL context dependencies - if(${component} STREQUAL EglContext) + if(${component} STREQUAL XEglContext) find_package(EGL) find_package(X11) if(EGL_FOUND AND X11_FOUND) @@ -137,6 +149,11 @@ foreach(component ${Magnum_FIND_COMPONENTS}) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES Cube.h) endif() + # Scene graph library + if(${component} STREQUAL SceneGraph) + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES Scene.h) + endif() + # Shaders library if(${component} STREQUAL Shaders) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES PhongShader.h) diff --git a/src/AbstractImage.h b/src/AbstractImage.h index 3dea087db..1a86e4313 100644 --- a/src/AbstractImage.h +++ b/src/AbstractImage.h @@ -23,7 +23,7 @@ namespace Magnum { -/** @ingroup textures +/** @brief Non-templated base for one-, two- or three-dimensional images See Image, BufferedImage, Trade::ImageData documentation for more information. @@ -44,6 +44,7 @@ class MAGNUM_EXPORT AbstractImage { */ /** @brief Color components */ + /** @todo Support *_INTEGER types */ enum class Components: GLenum { #ifndef MAGNUM_TARGET_GLES /** diff --git a/src/AbstractShaderProgram.cpp b/src/AbstractShaderProgram.cpp index 4fd606973..47ea3eeb4 100644 --- a/src/AbstractShaderProgram.cpp +++ b/src/AbstractShaderProgram.cpp @@ -50,6 +50,11 @@ void AbstractShaderProgram::bindFragmentDataLocation(GLuint location, const std: glBindFragDataLocation(program, location, name.c_str()); } +void AbstractShaderProgram::bindFragmentDataLocationIndexed(GLuint location, GLuint index, const std::string& name) { + CORRADE_ASSERT(state == Initialized, "AbstractShaderProgram: fragment data location cannot be bound after linking.", ); + + glBindFragDataLocationIndexed(program, location, index, name.c_str()); +} #endif void AbstractShaderProgram::link() { diff --git a/src/AbstractShaderProgram.h b/src/AbstractShaderProgram.h index d15c2aeb9..f50d20040 100644 --- a/src/AbstractShaderProgram.h +++ b/src/AbstractShaderProgram.h @@ -25,7 +25,7 @@ namespace Magnum { -/** @ingroup rendering +/** @brief Base class for shaders @section AbstractShaderProgram-subclassing Subclassing workflow @@ -39,6 +39,8 @@ typedef Attribute<0, Vector4> Vertex; typedef Attribute<1, Vector3> Normal; typedef Attribute<2, Vector2> TextureCoords; @endcode + @todoc Output attribute location (for bindFragmentDataLocationIndexed(), + referenced also from Framebuffer::mapDefaultForDraw() / Framebuffer::mapForDraw()) - **Layers for texture uniforms** to which the textures will be bound before rendering, for example: @code @@ -78,22 +80,33 @@ The preferred workflow is to specify attribute location for vertex shader input attributes and fragment shader output attributes explicitly in the shader code, e.g.: @code +#version 330 +// or #extension GL_ARB_explicit_attrib_location: enable layout(location = 0) in vec4 vertex; layout(location = 1) in vec3 normal; layout(location = 2) in vec2 textureCoords; @endcode +Similarly for ouput attributes, you can also specify blend equation color +index for them (see Framebuffer::BlendFunction for more information about +using color input index): +@code +layout(location = 0, index = 0) out vec4 color; +layout(location = 1, index = 1) out vec4 ambient; +@endcode @requires_gl (for explicit input attribute location instead of using bindAttributeLocation()) @requires_gl (for explicit output attribute location or using - bindFragmentDataLocation()) + bindFragmentDataLocation() / bindFragmentDataLocationIndexed()) @requires_gl30 Extension @extension{EXT,gpu_shader4} (for using bindFragmentDataLocation()) +@requires_gl33 Extension @extension{ARB,blend_func_extended} (for using + bindFragmentDataLocationIndexed()) @requires_gl33 Extension @extension{ARB,explicit_attrib_location} (for explicit attribute location instead of using bindAttributeLocation()) If you don't have the required extension, you can use functions bindAttributeLocation() -and bindFragmentDataLocation() between attaching of shaders and linking the -program: +and bindFragmentDataLocation() / bindFragmentDataLocationIndexed() between +attaching the shaders and linking the program: @code // Shaders attached... @@ -101,6 +114,9 @@ bindAttributeLocation(Vertex::Location, "vertex"); bindAttributeLocation(Normal::Location, "normal"); bindAttributeLocation(TextureCoords::Location, "textureCoords"); +bindFragmentDataLocationIndexed(0, 0, "color"); +bindFragmentDataLocationIndexed(1, 1, "ambient"); + // Link... @endcode @@ -108,6 +124,8 @@ bindAttributeLocation(TextureCoords::Location, "textureCoords"); The preferred workflow is to specify texture layers directly in the shader code, e.g.: @code +#version 420 +// or #extension GL_ARB_shading_language_420pack: enable layout(binding = 0) uniform sampler2D diffuseTexture; layout(binding = 1) uniform sampler2D specularTexture; @endcode @@ -236,8 +254,8 @@ class MAGNUM_EXPORT AbstractShaderProgram { * @param location Location * @param name Attribute name * - * Binds attribute to location which is be used later for binding - * vertex buffers. Preferred usage is to + * Binds attribute to location which is used later for binding vertex + * buffers. * @note This function should be called after attachShader() calls and * before link(). * @deprecated Preferred usage is to specify attribute location @@ -249,16 +267,32 @@ class MAGNUM_EXPORT AbstractShaderProgram { #ifndef MAGNUM_TARGET_GLES /** - * @brief Bind fragment data to given location + * @brief Bind fragment data to given location and color input index * @param location Location * @param name Fragment output variable name + * @param index Blend equation color input index (`0` or `1`) * + * Binds fragment data to location which is used later for framebuffer + * operations. See also Framebuffer::BlendFunction for more + * information about using color input index. + * @requires_gl + * @requires_gl33 Extension @extension{ARB,blend_func_extended} * @note This function should be called after attachShader() calls and * before link(). * @deprecated Preferred usage is to specify attribute location * explicitly in the shader instead of using this function. See * @ref AbstractShaderProgram-attribute-location "class documentation" * for more information. + */ + void bindFragmentDataLocationIndexed(GLuint location, GLuint index, const std::string& name); + + /** + * @brief Bind fragment data to given location and first color input index + * @param location Location + * @param name Fragment output variable name + * + * The same as bindFragmentDataLocationIndexed(), but with `index` set + * to `0`. * @requires_gl * @requires_gl30 Extension @extension{EXT,gpu_shader4} */ diff --git a/src/AbstractTexture.h b/src/AbstractTexture.h index cae04f499..5dd213d17 100644 --- a/src/AbstractTexture.h +++ b/src/AbstractTexture.h @@ -24,7 +24,7 @@ namespace Magnum { -/** @ingroup textures +/** @brief Base for textures @attention Don't forget to call @ref Texture::setWrapping() "setWrapping()", @@ -122,12 +122,14 @@ class MAGNUM_EXPORT AbstractTexture { /** * Red component only. Green and blue are set to `0`, alpha is set * to `1`. + * @requires_gl30 Extension @extension{ARB,texture_rg} */ Red, /** * Red and green component. Blue is set to `0`, alpha is set to * `1`. + * @requires_gl30 Extension @extension{ARB,texture_rg} */ RedGreen, @@ -145,85 +147,75 @@ class MAGNUM_EXPORT AbstractTexture { enum class ComponentType { /** * (Non-normalized) unsigned byte - * * @requires_gl30 Extension @extension{EXT,texture_integer} */ UnsignedByte, /** * (Non-normalized) byte - * * @requires_gl30 Extension @extension{EXT,texture_integer} */ Byte, /** * (Non-normalized) unsigned short - * * @requires_gl30 Extension @extension{EXT,texture_integer} */ UnsignedShort, /** * (Non-normalized) short - * * @requires_gl30 Extension @extension{EXT,texture_integer} */ Short, /** * (Non-normalized) unsigned integer - * * @requires_gl30 Extension @extension{EXT,texture_integer} */ UnsignedInt, /** * (Non-normalized) integer - * * @requires_gl30 Extension @extension{EXT,texture_integer} */ Int, /** * Half float (16 bit) - * * @requires_gl30 Extension @extension{ARB,texture_float} */ Half, /** * Float (32 bit) - * * @requires_gl30 Extension @extension{ARB,texture_float} */ Float, /** - * Normalized unsigned byte, i.e. values from range - * @f$ [0; 255] @f$ are converted to range @f$ [0.0; 1.0] @f$. + * Normalized unsigned byte, i.e. values from range @f$ [0; 255] @f$ + * are converted to range @f$ [0.0; 1.0] @f$. */ NormalizedUnsignedByte, /** - * Normalized byte, i.e. values from range - * @f$ [-128; 127] @f$ are converted to range @f$ [0.0; 1.0] @f$. - * - * @requires_gl31 (no extension providing this functionality) + * Normalized signed byte, i.e. values from range @f$ [-128; 127] @f$ + * are converted to range @f$ [-1.0; 1.0] @f$. + * @requires_gl31 Extension @extension{EXT,texture_snorm} */ NormalizedByte, /** - * Normalized unsigned short, i.e. values from range - * @f$ [0; 65536] @f$ are converted to range @f$ [0.0; 1.0] @f$. + * Normalized unsigned short, i.e. values from range @f$ [0; 65536] @f$ + * are converted to range @f$ [0.0; 1.0] @f$. */ NormalizedUnsignedShort, /** - * Normalized short, i.e. values from range - * @f$ [-32768; 32767] @f$ are converted to range @f$ [0.0; 1.0] @f$. - * - * @requires_gl31 (no extension providing this functionality) + * Normalized signed short, i.e. values from range @f$ [-32768; 32767] @f$ + * are converted to range @f$ [-1.0; 1.0] @f$. + * @requires_gl31 Extension @extension{EXT,texture_snorm} */ NormalizedShort }; @@ -241,7 +233,7 @@ class MAGNUM_EXPORT AbstractTexture { * One-component (red channel), unsigned normalized, probably * 8bit. * @requires_gl - * @requires_gl30 (no extension providing this functionality) + * @requires_gl30 Extension @extension{ARB,texture_rg} */ Red = GL_RED, @@ -249,7 +241,7 @@ class MAGNUM_EXPORT AbstractTexture { * Two-component (red and green channel), unsigned normalized, * each component probably 8bit, 16bit total. * @requires_gl - * @requires_gl30 (no extension providing this functionality) + * @requires_gl30 Extension @extension{ARB,texture_rg} */ RedGreen = GL_RG, #endif @@ -365,12 +357,14 @@ class MAGNUM_EXPORT AbstractTexture { /** * Compressed red channel, unsigned normalized. * @requires_gl + * @requires_gl30 Extension @extension{ARB,texture_rg} */ CompressedRed = GL_COMPRESSED_RED, /** * Compressed red and green channel, unsigned normalized. * @requires_gl + * @requires_gl30 Extension @extension{ARB,texture_rg} */ CompressedRedGreen = GL_COMPRESSED_RG, diff --git a/src/Buffer.h b/src/Buffer.h index 940dcaae5..c76260f3e 100644 --- a/src/Buffer.h +++ b/src/Buffer.h @@ -23,7 +23,7 @@ namespace Magnum { -/** @ingroup rendering +/** @brief Class for managing buffers @todo Support for buffer copying (OpenGL 3.1, @extension{ARB,copy_buffer}) @@ -69,7 +69,10 @@ class Buffer { */ PixelUnpack = GL_PIXEL_UNPACK_BUFFER, - /** Target for pixel pack operations. */ + /** + * Target for pixel pack operations. + * @requires_gl + */ PixelPack = GL_PIXEL_PACK_BUFFER, /** diff --git a/src/BufferedImage.h b/src/BufferedImage.h index c8efa5181..5489716df 100644 --- a/src/BufferedImage.h +++ b/src/BufferedImage.h @@ -16,7 +16,7 @@ */ /** @file - * @brief Class Magnum::BufferedImage + * @brief Class Magnum::BufferedImage, typedef Magnum::BufferedImage1D, Magnum::BufferedImage2D, Magnum::BufferedImage3D */ #include "AbstractImage.h" @@ -26,10 +26,6 @@ namespace Magnum { #ifndef MAGNUM_TARGET_GLES -/** @addtogroup textures - * @{ - */ - /** @brief %Buffered image @@ -116,8 +112,6 @@ typedef BufferedImage<2> BufferedImage2D; /** @brief Three-dimensional buffered image */ typedef BufferedImage<3> BufferedImage3D; - -/*@}*/ #endif } diff --git a/src/BufferedTexture.h b/src/BufferedTexture.h index fca8f74fc..12f2b05c0 100644 --- a/src/BufferedTexture.h +++ b/src/BufferedTexture.h @@ -25,7 +25,7 @@ namespace Magnum { #ifndef MAGNUM_TARGET_GLES -/** @ingroup textures +/** @brief Buffered texture This texture is, unlike classic textures such as Texture or CubeMapTexture, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 026bbcfbe..ee32fe700 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,10 +33,8 @@ set(Magnum_SRCS AbstractTexture.cpp AbstractShaderProgram.cpp BufferedTexture.cpp - Camera.cpp Framebuffer.cpp IndexedMesh.cpp - Light.cpp Mesh.cpp Query.cpp Renderbuffer.cpp @@ -53,7 +51,6 @@ set(Magnum_HEADERS BufferedImage.h BufferedTexture.h Buffer.h - Camera.h Color.h CubeMapTextureArray.h CubeMapTexture.h @@ -61,15 +58,13 @@ set(Magnum_HEADERS Image.h ImageWrapper.h IndexedMesh.h - Light.h Magnum.h Mesh.h - Object.h Query.h Renderbuffer.h - Scene.h Shader.h SizeTraits.h + Swizzle.h Texture.h TypeTraits.h @@ -86,10 +81,6 @@ if(NOT CMAKE_NO_OBJECT_TARGET) add_library(MagnumMathObjects OBJECT ${MagnumMath_SRCS}) endif() -# Files compiled with different flags for main library and unit test library -set(Magnum_GracefulAssert_SRCS - Object.cpp) - # Set shared library flags for the objects, as they will be part of shared lib # TODO: fix when CMake sets target_EXPORTS for OBJECT targets as well if(NOT CMAKE_NO_OBJECT_TARGET) @@ -100,13 +91,11 @@ endif() if(NOT CMAKE_NO_OBJECT_TARGET) add_library(Magnum SHARED $ - $ - ${Magnum_GracefulAssert_SRCS}) + $) else() add_library(Magnum SHARED ${Magnum_SRCS} - ${MagnumMath_SRCS} - ${Magnum_GracefulAssert_SRCS}) + ${MagnumMath_SRCS}) endif() target_link_libraries(Magnum ${CORRADE_UTILITY_LIBRARY} ${CORRADE_PLUGINMANAGER_LIBRARY}) if(NOT TARGET_GLES) @@ -137,6 +126,10 @@ if(WITH_PRIMITIVES) add_subdirectory(Primitives) endif() +if(WITH_SCENEGRAPH) + add_subdirectory(SceneGraph) +endif() + if(WITH_SHADERS) add_subdirectory(Shaders) endif() @@ -147,12 +140,10 @@ if(BUILD_TESTS) # Library with graceful assert for testing if(NOT CMAKE_NO_OBJECT_TARGET) add_library(MagnumTestLib SHARED - $ - ${Magnum_GracefulAssert_SRCS}) + $) else() add_library(MagnumTestLib SHARED - ${Magnum_SRCS} - ${Magnum_GracefulAssert_SRCS}) + ${Magnum_SRCS}) endif() set_target_properties(MagnumTestLib PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) target_link_libraries(MagnumTestLib ${CORRADE_UTILITY_LIBRARY} ${CORRADE_PLUGINMANAGER_LIBRARY}) diff --git a/src/Camera.cpp b/src/Camera.cpp deleted file mode 100644 index f15454a27..000000000 --- a/src/Camera.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - Copyright © 2010, 2011, 2012 Vladimír Vondruš - - This file is part of Magnum. - - Magnum is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 3 - only, as published by the Free Software Foundation. - - Magnum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License version 3 for more details. -*/ - -#include "Camera.h" -#include "Framebuffer.h" -#include "Scene.h" - -using namespace std; - -namespace Magnum { - -Camera::Camera(Object* parent): Object(parent), _aspectRatioPolicy(AspectRatioPolicy::Extend) {} - -void Camera::setOrthographic(GLfloat size, GLfloat near, GLfloat far) { - _near = near; - _far = far; - - /* Scale the volume down so it fits in (-1, 1) in all directions */ - GLfloat xyScale = 2/size; - GLfloat zScale = 2/(far-near); - rawProjectionMatrix = Matrix4::scaling({xyScale, xyScale, -zScale}); - - /* Move the volume on z into (-1, 1) range */ - rawProjectionMatrix = Matrix4::translation(Vector3::zAxis(-1-near*zScale))*rawProjectionMatrix; - - fixAspectRatio(); -} - -void Camera::setPerspective(GLfloat fov, GLfloat near, GLfloat far) { - _near = near; - _far = far; - - /* First move the volume on z in (-1, 1) range */ - rawProjectionMatrix = Matrix4::translation(Vector3::zAxis(2*far*near/(far+near))); - - /* Then apply magic perspective matrix (with reversed Z) */ - static const Matrix4 a(1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, -1.0f, -1.0f, - 0.0f, 0.0f, 0.0f, 0.0f); - rawProjectionMatrix = a*rawProjectionMatrix; - - /* Then scale the volume down so it fits in (-1, 1) in all directions */ - GLfloat xyScale = 1/tan(fov/2); - GLfloat zScale = 1+2*near/(far-near); - rawProjectionMatrix = Matrix4::scaling({xyScale, xyScale, zScale})*rawProjectionMatrix; - - /* And... another magic */ - rawProjectionMatrix[3][3] = 0; - - fixAspectRatio(); -} - -void Camera::setViewport(const Math::Vector2& size) { - Framebuffer::setViewport({0, 0}, size); - - _viewport = size; - fixAspectRatio(); -} - -void Camera::clean(const Matrix4& absoluteTransformation) { - Object::clean(absoluteTransformation); - - _cameraMatrix = absoluteTransformation.inverted(); -} - -void Camera::fixAspectRatio() { - /* Don't divide by zero */ - if(_viewport.x() == 0 || _viewport.y() == 0) { - _projectionMatrix = rawProjectionMatrix; - return; - } - - /* Extend on larger side = scale larger side down */ - if(_aspectRatioPolicy == AspectRatioPolicy::Extend) { - _projectionMatrix = ((_viewport.x() > _viewport.y()) ? - Matrix4::scaling({GLfloat(_viewport.y())/_viewport.x(), 1, 1}) : - Matrix4::scaling({1, GLfloat(_viewport.x())/_viewport.y(), 1}) - )*rawProjectionMatrix; - - /* Clip on smaller side = scale smaller side up */ - } else if(_aspectRatioPolicy == AspectRatioPolicy::Clip) { - _projectionMatrix = ((_viewport.x() > _viewport.y()) ? - Matrix4::scaling({1, GLfloat(_viewport.x())/_viewport.y(), 1}) : - Matrix4::scaling({GLfloat(_viewport.y())/_viewport.x(), 1, 1}) - )*rawProjectionMatrix; - - /* Don't preserve anything */ - } else _projectionMatrix = rawProjectionMatrix; -} - -void Camera::draw() { - Scene* s = scene(); - CORRADE_ASSERT(s, "Camera: cannot draw without camera attached to scene", ); - - Framebuffer::clear(); - - /* Recursively draw child objects */ - drawChildren(s, cameraMatrix()); -} - -void Camera::drawChildren(Object* object, const Matrix4& transformationMatrix) { - for(set::const_iterator it = object->children().begin(); it != object->children().end(); ++it) { - /* Transformation matrix for the object */ - Matrix4 matrix = transformationMatrix*(*it)->transformation(); - - /* Draw the object and its children */ - (*it)->draw(matrix, this); - drawChildren(*it, matrix); - } -} - -} diff --git a/src/Camera.h b/src/Camera.h deleted file mode 100644 index 833993e96..000000000 --- a/src/Camera.h +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef Magnum_Camera_h -#define Magnum_Camera_h -/* - Copyright © 2010, 2011, 2012 Vladimír Vondruš - - This file is part of Magnum. - - Magnum is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 3 - only, as published by the Free Software Foundation. - - Magnum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License version 3 for more details. -*/ - -/** @file - * @brief Class Magnum::Camera - */ - -#include "Object.h" - -#ifdef WIN32 /* I so HATE windows.h */ -#undef near -#undef far -#endif - -namespace Magnum { - -/** @ingroup scene -@brief %Camera object - */ -class MAGNUM_EXPORT Camera: public Object { - public: - /** - * @brief Aspect ratio policy - * - * @see aspectRatioPolicy(), setAspectRatioPolicy() - */ - enum class AspectRatioPolicy { - NotPreserved, /**< Don't preserve aspect ratio */ - Extend, /**< Extend on larger side of view */ - Clip /**< Clip on smaller side of view */ - }; - - /** - * @brief Constructor - * @param parent Parent object - * - * Sets orthographic projection to the default OpenGL cube (range - * @f$ [-1; 1] @f$ in all directions) and clear color to black. - * @see setOrthographic(), setPerspective() - */ - Camera(Object* parent = nullptr); - - /** @brief Aspect ratio policy */ - inline AspectRatioPolicy aspectRatioPolicy() const { return _aspectRatioPolicy; } - - /** @brief Set aspect ratio policy */ - void setAspectRatioPolicy(AspectRatioPolicy policy) { _aspectRatioPolicy = policy; } - - /** - * @brief Set orthographic projection - * @param size Size of (square) view - * @param near Near clipping plane - * @param far Far clipping plane - * - * The volume of given size will be scaled down to range - * @f$ [-1; 1] @f$ on all directions. - */ - void setOrthographic(GLfloat size, GLfloat near, GLfloat far); - - /** - * @brief Set perspective projection - * @param fov Field of view angle - * @param near Near clipping plane - * @param far Far clipping plane - */ - void setPerspective(GLfloat fov, GLfloat near, GLfloat far); - - /** @brief Near clipping plane */ - inline GLfloat near() const { return _near; } - - /** @brief Far clipping plane */ - inline GLfloat far() const { return _far; } - - /** - * @brief Camera matrix - * - * Camera matrix describes world position relative to the camera and is - * applied as first. - */ - inline Matrix4 cameraMatrix() { - setClean(); - return _cameraMatrix; - } - - /** - * @brief Projection matrix - * - * Projection matrix handles e.g. perspective distortion and is applied - * as last. - */ - inline Matrix4 projectionMatrix() const { return _projectionMatrix; } - - /** @brief Viewport size */ - inline Math::Vector2 viewport() const { return _viewport; } - - /** - * @brief Set viewport size - * - * Call when window size changes. - * - * Calls Framebuffer::setViewport() and stores viewport size - * internally. - */ - virtual void setViewport(const Math::Vector2& size); - - /** - * @brief Draw the scene - * - * Calls Framebuffer::clear() and draws the scene using drawChildren(). - */ - virtual void draw(); - - using Object::draw; /* Don't hide Object's draw() */ - - protected: - /** - * Recalculates camera matrix. - */ - void clean(const Matrix4& absoluteTransformation); - - /** - * @brief Draw object children - * - * Recursively draws all children of the object. - */ - void drawChildren(Object* object, const Matrix4& transformationMatrix); - - private: - Matrix4 rawProjectionMatrix; - Matrix4 _projectionMatrix; - Matrix4 _cameraMatrix; - GLfloat _near, _far; - - Math::Vector2 _viewport; - AspectRatioPolicy _aspectRatioPolicy; - - MAGNUM_LOCAL void fixAspectRatio(); -}; - -} - -#endif diff --git a/src/Color.h b/src/Color.h index 7dd384a1f..c95e010a3 100644 --- a/src/Color.h +++ b/src/Color.h @@ -140,6 +140,8 @@ is always in range in range @f$ [0.0, 360.0] @f$, saturation and value in range @f$ [0.0, 1.0] @f$. @see Color4 + +@todo Signed normalization to [-1.0, 1.0] like in OpenGL? */ template class Color3: public Math::Vector3 { public: @@ -394,16 +396,24 @@ template class Color4: public Math::Vector4 { } }; -/** @debugoperator{Color3} */ +/** @debugoperator{Magnum::Color3} */ template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Color3& value) { return debug << static_cast&>(value); } -/** @debugoperator{Color4} */ +/** @debugoperator{Magnum::Color4} */ template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Color4& value) { return debug << static_cast&>(value); } } +namespace Corrade { namespace Utility { + /** @configurationvalue{Magnum::Color3} */ + template struct ConfigurationValue>: public ConfigurationValue> {}; + + /** @configurationvalue{Magnum::Color4} */ + template struct ConfigurationValue>: public ConfigurationValue> {}; +}} + #endif diff --git a/src/Contexts/AbstractGlInterface.h b/src/Contexts/AbstractGlInterface.h new file mode 100644 index 000000000..3f6d14ef6 --- /dev/null +++ b/src/Contexts/AbstractGlInterface.h @@ -0,0 +1,64 @@ +#ifndef Magnum_Contexts_AbstractGlInterface_h +#define Magnum_Contexts_AbstractGlInterface_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::Contexts::AbstractGlInterface + */ + +#include "ExtensionWrangler.h" + +namespace Magnum { namespace Contexts { + +/** @brief Base for OpenGL interfaces */ +template class AbstractGlInterface { + public: + /** + * @brief Get visual ID + * + * Initializes the interface on given display and returns visual ID. + */ + virtual VisualId getVisualId(Display nativeDisplay) = 0; + + /** + * @brief Destructor + * + * Finalizes and closes the interface. + */ + virtual ~AbstractGlInterface() {} + + /** @brief Create context */ + virtual void createContext(Window nativeWindow) = 0; + + /** + * @brief Whether to enable experimental extension wrangler features + * + * Default is to disable. + */ + virtual inline ExtensionWrangler::ExperimentalFeatures experimentalExtensionWranglerFeatures() const { + return ExtensionWrangler::ExperimentalFeatures::Disable; + } + + /** @brief Make the context current */ + virtual void makeCurrent() = 0; + + /** @brief Swap buffers */ + virtual void swapBuffers() = 0; +}; + +}} + +#endif diff --git a/src/Contexts/AbstractXContext.cpp b/src/Contexts/AbstractXContext.cpp new file mode 100644 index 000000000..9c0747aaf --- /dev/null +++ b/src/Contexts/AbstractXContext.cpp @@ -0,0 +1,142 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "AbstractXContext.h" + +#include "ExtensionWrangler.h" + +#define None 0L // redef Xlib nonsense + +/* Mask for X events */ +#define INPUT_MASK KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|PointerMotionMask|StructureNotifyMask + +using namespace std; + +namespace Magnum { namespace Contexts { + +AbstractXContext::AbstractXContext(AbstractGlInterface* glInterface, int&, char**, const string& title, const Math::Vector2& size): glInterface(glInterface), viewportSize(size), flags(Flag::Redraw) { + /* Get default X display */ + display = XOpenDisplay(0); + + /* Get visual ID */ + VisualID visualId = glInterface->getVisualId(display); + + /* Get visual info */ + XVisualInfo *visInfo, visTemplate; + int visualCount; + visTemplate.visualid = visualId; + visInfo = XGetVisualInfo(display, VisualIDMask, &visTemplate, &visualCount); + if(!visInfo) { + Error() << "Cannot get X visual"; + ::exit(1); + } + + /* Create X Window */ + Window root = RootWindow(display, DefaultScreen(display)); + XSetWindowAttributes attr; + attr.background_pixel = 0; + attr.border_pixel = 0; + attr.colormap = XCreateColormap(display, root, visInfo->visual, AllocNone); + attr.event_mask = 0; + unsigned long mask = CWBackPixel|CWBorderPixel|CWColormap|CWEventMask; + window = XCreateWindow(display, root, 20, 20, size.x(), size.y(), 0, visInfo->depth, InputOutput, visInfo->visual, mask, &attr); + XSetStandardProperties(display, window, title.c_str(), 0, None, 0, 0, 0); + XFree(visInfo); + + /* Be notified about closing the window */ + deleteWindow = XInternAtom(display, "WM_DELETE_WINDOW", True); + XSetWMProtocols(display, window, &deleteWindow, 1); + + /* Create context */ + glInterface->createContext(window); + + /* Capture exposure, keyboard and mouse button events */ + XSelectInput(display, window, INPUT_MASK); + + /* Set OpenGL context as current */ + glInterface->makeCurrent(); + + /* Initialize extension wrangler */ + ExtensionWrangler::initialize(glInterface->experimentalExtensionWranglerFeatures()); +} + +AbstractXContext::~AbstractXContext() { + /* Shut down the interface */ + delete glInterface; + + /* Shut down X */ + XDestroyWindow(display, window); + XCloseDisplay(display); +} + +int AbstractXContext::exec() { + /* Show window */ + XMapWindow(display, window); + + /* Call viewportEvent for the first time */ + viewportEvent(viewportSize); + + while(!(flags & Flag::Exit)) { + XEvent event; + + /* Closed window */ + if(XCheckTypedWindowEvent(display, window, ClientMessage, &event) && + Atom(event.xclient.data.l[0]) == deleteWindow) { + return 0; + } + + while(XCheckWindowEvent(display, window, INPUT_MASK, &event)) { + switch(event.type) { + /* Window resizing */ + case ConfigureNotify: { + Math::Vector2 size(event.xconfigure.width, event.xconfigure.height); + if(size != viewportSize) { + viewportSize = size; + viewportEvent(size); + flags |= Flag::Redraw; + } + } break; + + /* Key/mouse events */ + case KeyPress: + keyPressEvent(static_cast(XLookupKeysym(&event.xkey, 0)), static_cast(event.xkey.state), {event.xkey.x, event.xkey.y}); + break; + case KeyRelease: + keyReleaseEvent(static_cast(XLookupKeysym(&event.xkey, 0)), static_cast(event.xkey.state), {event.xkey.x, event.xkey.y}); + break; + case ButtonPress: + mousePressEvent(static_cast(event.xbutton.button), static_cast(event.xkey.state), {event.xbutton.x, event.xbutton.y}); + break; + case ButtonRelease: + mouseReleaseEvent(static_cast(event.xbutton.button), static_cast(event.xkey.state), {event.xbutton.x, event.xbutton.y}); + break; + + /* Mouse move events */ + case MotionNotify: + mouseMotionEvent(static_cast(event.xmotion.state), {event.xmotion.x, event.xmotion.y}); + break; + } + } + + if(flags & Flag::Redraw) { + flags &= ~Flag::Redraw; + drawEvent(); + } else Corrade::Utility::sleep(5); + } + + return 0; +} + +}} diff --git a/src/Contexts/EglContext.h b/src/Contexts/AbstractXContext.h similarity index 58% rename from src/Contexts/EglContext.h rename to src/Contexts/AbstractXContext.h index 1c1c7f628..620a777ac 100644 --- a/src/Contexts/EglContext.h +++ b/src/Contexts/AbstractXContext.h @@ -1,5 +1,5 @@ -#ifndef Magnum_Contexts_EglContext_h -#define Magnum_Contexts_EglContext_h +#ifndef Magnum_Contexts_AbstractXContext_h +#define Magnum_Contexts_AbstractXContext_h /* Copyright © 2010, 2011, 2012 Vladimír Vondruš @@ -16,52 +16,57 @@ */ /** @file - * @brief Class Magnum::Contexts::EglContext + * @brief Class Magnum::Contexts::AbstractXContext */ #include "Magnum.h" #include -#ifdef None // undef Xlib nonsense to avoid conflicts +#include +/* undef Xlib nonsense to avoid conflicts */ #undef None -#endif +#undef Always -#ifndef SUPPORT_X11 -#define SUPPORT_X11 // OpenGL ES on BeagleBoard needs this (?) -#endif -#include +#include #include "AbstractContext.h" +#include "AbstractGlInterface.h" namespace Magnum { namespace Contexts { -/** -@brief X/EGL context +/** @nosubgrouping +@brief Base for X11-based contexts Supports keyboard and mouse handling. + +@note Not meant to be used directly, see subclasses. */ -class EglContext: public AbstractContext { +class AbstractXContext: public AbstractContext { public: /** * @brief Constructor - * @param argc Count of arguments of `main()` function - * @param argv Arguments of `main()` function - * @param title Window title - * @param size Window size + * @param glInterface Interface to OpenGL + * @param argc Count of arguments of `main()` function + * @param argv Arguments of `main()` function + * @param title Window title + * @param size Window size * * Creates window with double-buffered OpenGL ES 2 context. */ - EglContext(int& argc, char** argv, const std::string& title = "Magnum X/EGL context", const Math::Vector2& size = Math::Vector2(800, 600)); + AbstractXContext(AbstractGlInterface* glInterface, int& argc, char** argv, const std::string& title = "Magnum X/EGL context", const Math::Vector2& size = Math::Vector2(800, 600)); /** * @brief Destructor * * Deletes context and destroys the window. */ - ~EglContext(); + virtual ~AbstractXContext() = 0; int exec(); + /** @brief Exit application main loop */ + inline void exit() { flags |= Flag::Exit; } + /** @{ @name Drawing functions */ protected: @@ -72,18 +77,50 @@ class EglContext: public AbstractContext { virtual void drawEvent() = 0; /** @copydoc GlutContext::swapBuffers() */ - inline void swapBuffers() { eglSwapBuffers(display, surface); } + inline void swapBuffers() { glInterface->swapBuffers(); } - /** @todo implement */ - inline void redraw() {} + /** @copydoc GlutContext::redraw() */ + inline void redraw() { flags |= Flag::Redraw; } /*@}*/ /** @{ @name Keyboard handling */ public: - /** @brief Key */ + /** + * @brief %Modifier + * + * @see Modifiers, keyPressEvent(), keyReleaseEvent(), + * mousePressEvent(), mouseReleaseEvent(), mouseMotionEvent() + */ + enum class Modifier: unsigned int { + Shift = ShiftMask, /**< Shift */ + CapsLock = LockMask, /**< Caps lock */ + Ctrl = ControlMask, /**< Ctrl */ + Alt = Mod1Mask, /**< Alt */ + NumLock = Mod2Mask, /**< Num lock */ + AltGr = Mod5Mask, /**< AltGr */ + + LeftButton = Button1Mask, /**< Left mouse button */ + MiddleButton = Button2Mask, /**< Middle mouse button */ + RightButton = Button3Mask /**< Right mouse button */ + }; + + /** + * @brief Set of modifiers + * + * @see keyPressEvent(), keyReleaseEvent() + */ + typedef Corrade::Containers::EnumSet Modifiers; + + /** + * @brief Key + * + * @see keyPressEvent(), keyReleaseEvent() + */ enum class Key: KeySym { + Esc = XK_Escape, /**< Escape */ + Up = XK_Up, /**< Up arrow */ Down = XK_Down, /**< Down arrow */ Left = XK_Left, /**< Left arrow */ @@ -157,27 +194,33 @@ class EglContext: public AbstractContext { /** * @brief Key press event * @param key Key pressed + * @param modifiers Active modifiers * @param position Cursor position * * Called when an key is pressed. Default implementation does nothing. */ - virtual void keyPressEvent(Key key, const Math::Vector2& position); + virtual void keyPressEvent(Key key, Modifiers modifiers, const Math::Vector2& position); /** * @brief Key press event * @param key Key released + * @param modifiers Active modifiers * @param position Cursor position * * Called when an key is released. Default implementation does nothing. */ - virtual void keyReleaseEvent(Key key, const Math::Vector2& position); + virtual void keyReleaseEvent(Key key, Modifiers modifiers, const Math::Vector2& position); /*@}*/ /** @{ @name Mouse handling */ public: - /** @brief Mouse button */ + /** + * @brief Mouse button + * + * @see mousePressEvent(), mouseReleaseEvent() + */ enum class MouseButton: unsigned int { Left = Button1, /**< Left button */ Middle = Button2, /**< Middle button */ @@ -189,40 +232,67 @@ class EglContext: public AbstractContext { protected: /** * @brief Mouse press event + * @param button Button pressed + * @param modifiers Active modifiers + * @param position Cursor position * * Called when mouse button is pressed. Default implementation does * nothing. */ - virtual void mousePressEvent(MouseButton button, const Math::Vector2& position); + virtual void mousePressEvent(MouseButton button, Modifiers modifiers, const Math::Vector2& position); /** * @brief Mouse release event + * @param button Button released + * @param modifiers Active modifiers + * @param position Cursor position * * Called when mouse button is released. Default implementation does * nothing. */ - virtual void mouseReleaseEvent(MouseButton button, const Math::Vector2& position); + virtual void mouseReleaseEvent(MouseButton button, Modifiers modifiers, const Math::Vector2& position); + + /** + * @brief Mouse motion event + * @param modifiers Active modifiers + * @param position Cursor position + * + * Called when mouse is moved. + */ + virtual void mouseMotionEvent(Modifiers modifiers, const Math::Vector2& position); /*@}*/ private: - Display* xDisplay; - Window xWindow; + enum class Flag: unsigned int { + Redraw = 1 << 0, + Exit = 1 << 1 + }; + + typedef Corrade::Containers::EnumSet Flags; + CORRADE_ENUMSET_FRIEND_OPERATORS(Flags) + + Display* display; + Window window; Atom deleteWindow; - EGLDisplay display; - EGLSurface surface; - EGLContext context; + AbstractGlInterface* glInterface; /** @todo Get this from the created window */ Math::Vector2 viewportSize; + + Flags flags; }; -inline void EglContext::keyPressEvent(EglContext::Key, const Math::Vector2&) {} -inline void EglContext::keyReleaseEvent(EglContext::Key, const Math::Vector2&) {} -inline void EglContext::mousePressEvent(EglContext::MouseButton, const Math::Vector2&) {} -inline void EglContext::mouseReleaseEvent(EglContext::MouseButton, const Math::Vector2&) {} +CORRADE_ENUMSET_OPERATORS(AbstractXContext::Modifiers) +CORRADE_ENUMSET_OPERATORS(AbstractXContext::Flags) +/* Implementations for inline functions with unused parameters */ +inline void AbstractXContext::keyPressEvent(Key, Modifiers, const Math::Vector2&) {} +inline void AbstractXContext::keyReleaseEvent(Key, Modifiers, const Math::Vector2&) {} +inline void AbstractXContext::mousePressEvent(MouseButton, Modifiers, const Math::Vector2&) {} +inline void AbstractXContext::mouseReleaseEvent(MouseButton, Modifiers, const Math::Vector2&) {} +inline void AbstractXContext::mouseMotionEvent(Modifiers, const Math::Vector2&) {} }} diff --git a/src/Contexts/CMakeLists.txt b/src/Contexts/CMakeLists.txt index 9adde18b6..7d5cd8fe0 100644 --- a/src/Contexts/CMakeLists.txt +++ b/src/Contexts/CMakeLists.txt @@ -1,10 +1,19 @@ -install(FILES AbstractContext.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Contexts) +# Extension wrangler +add_library(MagnumContextsExtensionWrangler OBJECT ExtensionWrangler.cpp) + +set(MagnumContexts_HEADERS + AbstractContext.h + AbstractGlInterface.h + ExtensionWrangler.h) +install(FILES ${MagnumContexts_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Contexts) # GLUT context if(WITH_GLUTCONTEXT) find_package(GLUT) if(GLUT_FOUND) - add_library(MagnumGlutContext STATIC GlutContext.cpp) + add_library(MagnumGlutContext STATIC + GlutContext.cpp + $) install(FILES GlutContext.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Contexts) install(TARGETS MagnumGlutContext DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) else() @@ -17,7 +26,9 @@ if(WITH_SDL2CONTEXT) find_package(SDL2) if(SDL2_FOUND) include_directories(${SDL2_INCLUDE_DIR}) - add_library(MagnumSdl2Context STATIC Sdl2Context.cpp) + add_library(MagnumSdl2Context STATIC + Sdl2Context.cpp + $) install(FILES Sdl2Context.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Contexts) install(TARGETS MagnumSdl2Context DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) else() @@ -25,17 +36,58 @@ if(WITH_SDL2CONTEXT) endif() endif() +# GLX context +if(WITH_GLXCONTEXT) + set(NEED_ABSTRACTXCONTEXT 1) + set(NEED_GLXINTERFACE 1) + add_library(MagnumGlxContext STATIC + $ + $ + $) + install(FILES GlxContext.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Contexts) + install(TARGETS MagnumGlxContext DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) +endif() + # X/EGL context -if(WITH_EGLCONTEXT) - find_package(EGL) +if(WITH_XEGLCONTEXT) + set(NEED_ABSTRACTXCONTEXT 1) + set(NEED_EGLINTERFACE 1) + add_library(MagnumXEglContext STATIC + $ + $ + $) + install(FILES XEglContext.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Contexts) + install(TARGETS MagnumXEglContext DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) +endif() + +# Abstract X context +if(NEED_ABSTRACTXCONTEXT) find_package(X11) - if(EGL_FOUND AND X11_FOUND) - add_library(MagnumEglContext STATIC EglContext.cpp) - # X11 macros are a mess, disable warnings for C-style casts - set_target_properties(MagnumEglContext PROPERTIES COMPILE_FLAGS "-Wno-old-style-cast") - install(FILES EglContext.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Contexts) - install(TARGETS MagnumEglContext DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) - else() - message(FATAL_ERROR "EGL or X11 libraries, required by EglContext, were not found. Set WITH_EGLCONTEXT to OFF to skip building it.") + if(NOT X11_FOUND) + message(FATAL_ERROR "X11 library, required by some contexts, was not found. Set WITH_*X*CONTEXT to OFF to skip building them.") + endif() + add_library(MagnumAbstractXContext OBJECT AbstractXContext.cpp) + # X11 macros are a mess, disable warnings for C-style casts + set_target_properties(MagnumAbstractXContext PROPERTIES COMPILE_FLAGS "-Wno-old-style-cast") + install(FILES AbstractXContext.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Contexts) +endif() + +# GLX interface +if(NEED_GLXINTERFACE) + add_library(MagnumGlxInterface OBJECT GlxInterface.cpp) + # X11 macros are a mess, disable warnings for C-style casts + set_target_properties(MagnumGlxInterface PROPERTIES COMPILE_FLAGS "-Wno-old-style-cast") + install(FILES GlxInterface.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Contexts) +endif() + +# EGL interface +if(NEED_EGLINTERFACE) + find_package(EGL) + if(NOT EGL_FOUND) + message(FATAL_ERROR "EGL library, required by some contexts, was not found. Set WITH_*EGL*CONTEXT to OFF to skip building them.") endif() + add_library(MagnumEglInterface OBJECT EglInterface.cpp) + # X11 macros are a mess, disable warnings for C-style casts + set_target_properties(MagnumEglInterface PROPERTIES COMPILE_FLAGS "-Wno-old-style-cast") + install(FILES EglInterface.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Contexts) endif() diff --git a/src/Contexts/EglContext.cpp b/src/Contexts/EglContext.cpp deleted file mode 100644 index b05890ab8..000000000 --- a/src/Contexts/EglContext.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - Copyright © 2010, 2011, 2012 Vladimír Vondruš - - This file is part of Magnum. - - Magnum is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 3 - only, as published by the Free Software Foundation. - - Magnum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License version 3 for more details. -*/ - -#include "EglContext.h" - -#define None 0L // redef Xlib nonsense - -/* Mask for X events */ -#define INPUT_MASK KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|StructureNotifyMask - -using namespace std; - -namespace Magnum { namespace Contexts { - -EglContext::EglContext(int&, char**, const string& title, const Math::Vector2& size): viewportSize(size) { - /* Get default X display and root window, init EGL */ - xDisplay = XOpenDisplay(0); - display = eglGetDisplay(xDisplay); - eglInitialize(display, 0, 0); - #ifndef MAGNUM_TARGET_GLES - eglBindAPI(EGL_OPENGL_API); - #else - eglBindAPI(EGL_OPENGL_ES_API); - #endif - int screenNumber = DefaultScreen(xDisplay); - Window root = RootWindow(xDisplay, screenNumber); - - /* Choose EGL config */ - static const EGLint attribs[] = { - EGL_RED_SIZE, 1, - EGL_GREEN_SIZE, 1, - EGL_BLUE_SIZE, 1, - EGL_DEPTH_SIZE, 1, - #ifndef MAGNUM_TARGET_GLES - EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, - #else - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - #endif - EGL_NONE - }; - EGLConfig config; - EGLint configCount; - if(!eglChooseConfig(display, attribs, &config, 1, &configCount)) { - Error() << "Cannot get EGL visual config"; - exit(1); - } - - /* Get X visual */ - EGLint visualId; - if(!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &visualId)) { - Error() << "Cannot get native visual ID"; - exit(1); - } - XVisualInfo *visInfo, visTemplate; - int visualCount; - visTemplate.visualid = visualId; - visInfo = XGetVisualInfo(xDisplay, VisualIDMask, &visTemplate, &visualCount); - if(!visInfo) { - Error() << "Cannot get X visual"; - exit(1); - } - - /* Create X Window */ - XSetWindowAttributes attr; - attr.background_pixel = 0; - attr.border_pixel = 0; - attr.colormap = XCreateColormap(xDisplay, root, visInfo->visual, AllocNone); - attr.event_mask = 0; - unsigned long mask = CWBackPixel|CWBorderPixel|CWColormap|CWEventMask; - xWindow = XCreateWindow(xDisplay, root, 20, 20, size.x(), size.y(), 0, visInfo->depth, InputOutput, visInfo->visual, mask, &attr); - XSetStandardProperties(xDisplay, xWindow, title.c_str(), 0, None, 0, 0, 0); - XFree(visInfo); - - /* Be notified about closing the window */ - deleteWindow = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True); - XSetWMProtocols(xDisplay, xWindow, &deleteWindow, 1); - - /* Create context and window surface */ - static const EGLint contextAttributes[] = { - #ifdef MAGNUM_TARGET_GLES - EGL_CONTEXT_CLIENT_VERSION, 2, - #endif - EGL_NONE - }; - context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes); - if(!context) { - Error() << "Cannot create EGL context"; - exit(1); - } - surface = eglCreateWindowSurface(display, config, xWindow, NULL); - if(!surface) { - Error() << "Cannot create window surface"; - exit(1); - } - - /* Capture exposure, keyboard and mouse button events */ - XSelectInput(xDisplay, xWindow, INPUT_MASK); - - /* Set OpenGL context as current */ - eglMakeCurrent(display, surface, surface, context); - - /** @bug Fixme: GLEW initialization fails (thinks that the context is not created) */ - #ifndef MAGNUM_TARGET_GLES - /* Init GLEW */ - GLenum err = glewInit(); - if(err != GLEW_OK) { - Error() << "EglContext: cannot initialize GLEW:" << glewGetErrorString(err); - exit(1); - } - #endif -} - -EglContext::~EglContext() { - /* Shut down EGL */ - eglDestroyContext(display, context); - eglDestroySurface(display, surface); - eglTerminate(display); - - /* Shut down X */ - XDestroyWindow(xDisplay, xWindow); - XCloseDisplay(xDisplay); -} - -int EglContext::exec() { - /* Show window */ - XMapWindow(xDisplay, xWindow); - - /* Call viewportEvent for the first time */ - viewportEvent(viewportSize); - - while(true) { - XEvent event; - - /* Closed window */ - if(XCheckTypedWindowEvent(xDisplay, xWindow, ClientMessage, &event) && - Atom(event.xclient.data.l[0]) == deleteWindow) { - return 0; - } - - while(XCheckWindowEvent(xDisplay, xWindow, INPUT_MASK, &event)) { - switch(event.type) { - /* Window resizing */ - case ConfigureNotify: { - Math::Vector2 size(event.xconfigure.width, event.xconfigure.height); - if(size != viewportSize) { - viewportSize = size; - viewportEvent(size); - } - } break; - - /* Key/mouse events */ - case KeyPress: - keyPressEvent(static_cast(XLookupKeysym(&event.xkey, 0)), {event.xkey.x, event.xkey.y}); - break; - case KeyRelease: - keyReleaseEvent(static_cast(XLookupKeysym(&event.xkey, 0)), {event.xkey.x, event.xkey.y}); - break; - case ButtonPress: - mousePressEvent(static_cast(event.xbutton.button), {event.xbutton.x, event.xbutton.y}); - break; - case ButtonRelease: - mouseReleaseEvent(static_cast(event.xbutton.button), {event.xbutton.x, event.xbutton.y}); - break; - } - } - - /** @todo Handle at least window closing and resizing */ - drawEvent(); - } - - return 0; -} - -}} diff --git a/src/Contexts/EglInterface.cpp b/src/Contexts/EglInterface.cpp new file mode 100644 index 000000000..609f37322 --- /dev/null +++ b/src/Contexts/EglInterface.cpp @@ -0,0 +1,86 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "EglInterface.h" + +namespace Magnum { namespace Contexts { + +EglInterface::~EglInterface() { + eglDestroyContext(display, context); + eglDestroySurface(display, surface); + eglTerminate(display); +} + +VisualId EglInterface::getVisualId(EGLNativeDisplayType nativeDisplay) { + /* Initialize */ + display = eglGetDisplay(nativeDisplay); + eglInitialize(display, 0, 0); + #ifndef MAGNUM_TARGET_GLES + eglBindAPI(EGL_OPENGL_API); + #else + eglBindAPI(EGL_OPENGL_ES_API); + #endif + + /* Choose EGL config */ + static const EGLint attribs[] = { + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_DEPTH_SIZE, 1, + #ifndef MAGNUM_TARGET_GLES + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + #else + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + #endif + EGL_NONE + }; + EGLint configCount; + if(!eglChooseConfig(display, attribs, &config, 1, &configCount)) { + Error() << "Cannot get EGL visual config"; + exit(1); + } + + /* Get visual ID */ + EGLint visualId; + if(!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &visualId)) { + Error() << "Cannot get native visual ID"; + exit(1); + } + + return visualId; +} + +void EglInterface::createContext(EGLNativeWindowType window) { + static const EGLint contextAttributes[] = { + #ifdef MAGNUM_TARGET_GLES + EGL_CONTEXT_CLIENT_VERSION, 2, + #endif + EGL_NONE + }; + context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes); + if(!context) { + Error() << "Cannot create EGL context"; + exit(1); + } + surface = eglCreateWindowSurface(display, config, window, NULL); + if(!surface) { + Error() << "Cannot create window surface"; + exit(1); + } + + /** @bug Fixme: On desktop OpenGL and Mesa EGL implementation OpenGL version is 1.0, which is wrong */ +} + +}} diff --git a/src/Contexts/EglInterface.h b/src/Contexts/EglInterface.h new file mode 100644 index 000000000..671d6df18 --- /dev/null +++ b/src/Contexts/EglInterface.h @@ -0,0 +1,71 @@ +#ifndef Magnum_Contexts_EglInterface_h +#define Magnum_Contexts_EglInterface_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::Contexts::EglInterface + */ + +#include "Magnum.h" + +#ifndef SUPPORT_X11 +#define SUPPORT_X11 // OpenGL ES on BeagleBoard needs this (?) +#endif +#include + +#include "AbstractGlInterface.h" + +namespace Magnum { namespace Contexts { + +#ifndef DOXYGEN_GENERATING_OUTPUT +/* EGL returns visual ID as int, but Xorg expects long unsigned int */ +#ifdef __unix__ +typedef VisualID VisualId; +#else +typedef EGLInt VisualId; +#endif +#endif + +/** +@brief EGL interface + +Used in XEglContext. +*/ +class EglInterface: public AbstractGlInterface { + public: + ~EglInterface(); + + VisualId getVisualId(EGLNativeDisplayType nativeDisplay); + void createContext(EGLNativeWindowType nativeWindow); + + inline void makeCurrent() { + eglMakeCurrent(display, surface, surface, context); + } + + inline void swapBuffers() { + eglSwapBuffers(display, surface); + } + + private: + EGLDisplay display; + EGLConfig config; + EGLSurface surface; + EGLContext context; +}; + +}} + +#endif diff --git a/src/Contexts/ExtensionWrangler.cpp b/src/Contexts/ExtensionWrangler.cpp new file mode 100644 index 000000000..4515624bd --- /dev/null +++ b/src/Contexts/ExtensionWrangler.cpp @@ -0,0 +1,37 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "ExtensionWrangler.h" + +#include "Magnum.h" + +namespace Magnum { namespace Contexts { + +void ExtensionWrangler::initialize(ExperimentalFeatures experimentalFeatures) { + #ifndef MAGNUM_TARGET_GLES + /* Enable experimental features */ + if(experimentalFeatures == ExperimentalFeatures::Enable) + glewExperimental = true; + + /* Init GLEW */ + GLenum err = glewInit(); + if(err != GLEW_OK) { + Error() << "ExtensionWrangler: cannot initialize GLEW:" << glewGetErrorString(err); + exit(1); + } + #endif +} + +}} diff --git a/src/Contexts/ExtensionWrangler.h b/src/Contexts/ExtensionWrangler.h new file mode 100644 index 000000000..04dba65d3 --- /dev/null +++ b/src/Contexts/ExtensionWrangler.h @@ -0,0 +1,39 @@ +#ifndef Magnum_Contexts_ExtensionWrangler_h +#define Magnum_Contexts_ExtensionWrangler_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::Contexts::ExtensionWrangler + */ + +namespace Magnum { namespace Contexts { + +/** @brief Extension wrangler interface */ +class ExtensionWrangler { + public: + /** @brief Whether to enable or disable experimental features */ + enum class ExperimentalFeatures { + Disable, + Enable + }; + + /** @brief Initialize extension wrangler */ + static void initialize(ExperimentalFeatures experimentalFeatures = ExperimentalFeatures::Disable); +}; + +}} + +#endif diff --git a/src/Contexts/GlutContext.cpp b/src/Contexts/GlutContext.cpp index a3d210e31..01307482d 100644 --- a/src/Contexts/GlutContext.cpp +++ b/src/Contexts/GlutContext.cpp @@ -15,6 +15,8 @@ #include "GlutContext.h" +#include "ExtensionWrangler.h" + namespace Magnum { namespace Contexts { GlutContext* GlutContext::instance = nullptr; @@ -35,12 +37,7 @@ GlutContext::GlutContext(int& argc, char** argv, const std::string& title, const glutMotionFunc(staticMouseMotionEvent); glutDisplayFunc(staticDrawEvent); - /* Init GLEW */ - GLenum err = glewInit(); - if(err != GLEW_OK) { - Error() << "GlutContext: cannot initialize GLEW:" << glewGetErrorString(err); - exit(1); - } + ExtensionWrangler::initialize(); } }} diff --git a/src/Contexts/GlutContext.h b/src/Contexts/GlutContext.h index 17a3c921c..73e32569f 100644 --- a/src/Contexts/GlutContext.h +++ b/src/Contexts/GlutContext.h @@ -60,7 +60,8 @@ class GlutContext: public AbstractContext { * @brief Viewport event * * Called when viewport size changes. You should pass the new size to - * Camera::viewport() function of your camera. + * Framebuffer::setViewport() or SceneGraph::Camera::setViewport(), + * if using scene graph. */ virtual void viewportEvent(const Math::Vector2& size) = 0; @@ -68,8 +69,9 @@ class GlutContext: public AbstractContext { * @brief Draw event * * Here implement your drawing functions, such as calling - * Camera::draw(). After drawing is finished, call swapBuffers(). If - * you want to draw immediately again, call also redraw(). + * SceneGraph::Camera::draw(). After drawing is finished, call + * swapBuffers(). If you want to draw immediately again, call also + * redraw(). */ virtual void drawEvent() = 0; @@ -97,7 +99,11 @@ class GlutContext: public AbstractContext { /** @{ @name Keyboard handling */ public: - /** @brief Key */ + /** + * @brief Key + * + * @see keyPressEvent() + */ enum class Key: int { Up = GLUT_KEY_UP, /**< Up arrow */ Down = GLUT_KEY_DOWN, /**< Down arrow */ @@ -136,7 +142,11 @@ class GlutContext: public AbstractContext { /** @{ @name Mouse handling */ public: - /** @brief Mouse button */ + /** + * @brief Mouse button + * + * @see mousePressEvent(), mouseReleaseEvent() + */ enum class MouseButton: int { Left = GLUT_LEFT_BUTTON, /**< Left button */ Middle = GLUT_MIDDLE_BUTTON, /**< Middle button */ @@ -145,7 +155,11 @@ class GlutContext: public AbstractContext { WheelDown = 4 /**< Wheel down */ }; - /** @brief Mouse cursor */ + /** + * @brief Mouse cursor + * + * @see setMouseCursor() + */ enum class MouseCursor: int { Default = GLUT_CURSOR_INHERIT, /**< Default cursor provided by parent window */ None = GLUT_CURSOR_NONE /**< No cursor */ @@ -231,9 +245,9 @@ class GlutContext: public AbstractContext { }; /* Implementations for inline functions with unused parameters */ -inline void GlutContext::keyPressEvent(GlutContext::Key, const Math::Vector2&) {} -inline void GlutContext::mousePressEvent(GlutContext::MouseButton, const Math::Vector2&) {} -inline void GlutContext::mouseReleaseEvent(GlutContext::MouseButton, const Math::Vector2&) {} +inline void GlutContext::keyPressEvent(Key, const Math::Vector2&) {} +inline void GlutContext::mousePressEvent(MouseButton, const Math::Vector2&) {} +inline void GlutContext::mouseReleaseEvent(MouseButton, const Math::Vector2&) {} inline void GlutContext::mouseMotionEvent(const Math::Vector2&) {} }} diff --git a/src/Contexts/GlxContext.h b/src/Contexts/GlxContext.h new file mode 100644 index 000000000..a11b503c7 --- /dev/null +++ b/src/Contexts/GlxContext.h @@ -0,0 +1,49 @@ +#ifndef Magnum_Contexts_GlxContext_h +#define Magnum_Contexts_GlxContext_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::Contexts::GlxContext + */ + +#include "AbstractXContext.h" +#include "GlxInterface.h" + +namespace Magnum { namespace Contexts { + +/** +@brief GLX context + +Uses GlxInterface. +*/ +class GlxContext: public AbstractXContext { + public: + /** + * @brief Constructor + * @param argc Count of arguments of `main()` function + * @param argv Arguments of `main()` function + * @param title Window title + * @param size Window size + * + * Creates window with double-buffered OpenGL 3.3 core context or + * OpenGL ES 2.0 context, if targetting OpenGL ES. + */ + inline GlxContext(int& argc, char** argv, const std::string& title = "Magnum GLX context", const Math::Vector2& size = Math::Vector2(800, 600)): AbstractXContext(new GlxInterface, argc, argv, title, size) {} +}; + +}} + +#endif diff --git a/src/Contexts/GlxInterface.cpp b/src/Contexts/GlxInterface.cpp new file mode 100644 index 000000000..a8fb92680 --- /dev/null +++ b/src/Contexts/GlxInterface.cpp @@ -0,0 +1,90 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "GlxInterface.h" + +#include + +namespace Magnum { namespace Contexts { + +VisualID GlxInterface::getVisualId(Display* nativeDisplay) { + display = nativeDisplay; + + /* Check version */ + int major, minor; + glXQueryVersion(nativeDisplay, &major, &minor); + if(major == 1 && minor < 4) { + Error() << "GlxInterface: GLX version 1.4 or greater is required."; + exit(1); + } + + /* Choose config */ + int configCount = 0; + static const int attributes[] = { + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_X_RENDERABLE, True, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_DOUBLEBUFFER, True, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + 0 + }; + configs = glXChooseFBConfig(nativeDisplay, DefaultScreen(nativeDisplay), attributes, &configCount); + if(!configCount) { + Error() << "GlxInterface: no supported framebuffer configuration found."; + exit(1); + } + + /* Get visual ID */ + XVisualInfo* info = glXGetVisualFromFBConfig(display, configs[0]); + VisualID visualId = info->visualid; + XFree(info); + + return visualId; +} + +void GlxInterface::createContext(Window nativeWindow) { + window = nativeWindow; + + GLint attributes[] = { + #ifndef MAGNUM_TARGET_GLES + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 3, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + #else + GLX_CONTEXT_MAJOR_VERSION_ARB, 2, + GLX_CONTEXT_MINOR_VERSION_ARB, 0, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_ES2_PROFILE_BIT_EXT, + #endif + 0 + }; + + /** @todo Use some extension wrangler for this, not GLEW, as it apparently needs context to create context, yo dawg wtf. */ + PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); + context = glXCreateContextAttribsARB(display, configs[0], 0, True, attributes); + XFree(configs); + if(!context) { + Error() << "GlxInterface: cannot create context."; + exit(1); + } +} + +GlxInterface::~GlxInterface() { + glXMakeCurrent(display, None, nullptr); + glXDestroyContext(display, context); +} + +}} diff --git a/src/Contexts/GlxInterface.h b/src/Contexts/GlxInterface.h new file mode 100644 index 000000000..e9f3177d8 --- /dev/null +++ b/src/Contexts/GlxInterface.h @@ -0,0 +1,66 @@ +#ifndef Magnum_Contexts_EglInterface_h +#define Magnum_Contexts_EglInterface_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::Contexts::GlxInterface + */ + +#include "Magnum.h" +#include + +#include "AbstractGlInterface.h" + +namespace Magnum { namespace Contexts { + +/** +@brief GLX interface + +Creates OpenGL 3.3 core context or OpenGL ES 2.0 context, if targetting +OpenGL ES. + +Used in GlxContext. +*/ +class GlxInterface: public AbstractGlInterface { + public: + ~GlxInterface(); + + VisualID getVisualId(Display* nativeDisplay); + void createContext(Window nativeWindow); + + /* This must be enabled, otherwise (on my NVidia) it crashes when creating VAO. WTF. */ + inline ExtensionWrangler::ExperimentalFeatures experimentalExtensionWranglerFeatures() const { + return ExtensionWrangler::ExperimentalFeatures::Enable; + } + + inline void makeCurrent() { + glXMakeCurrent(display, window, context); + } + + inline void swapBuffers() { + glXSwapBuffers(display, window); + } + + private: + Display* display; + Window window; + GLXFBConfig* configs; + GLXContext context; +}; + +}} + +#endif diff --git a/src/Contexts/Sdl2Context.cpp b/src/Contexts/Sdl2Context.cpp index 472b9fc95..f55fa6125 100644 --- a/src/Contexts/Sdl2Context.cpp +++ b/src/Contexts/Sdl2Context.cpp @@ -14,6 +14,7 @@ */ #include "Sdl2Context.h" +#include "ExtensionWrangler.h" namespace Magnum { namespace Contexts { @@ -40,18 +41,9 @@ Sdl2Context::Sdl2Context(int, char**, const std::string& name, const Math::Vecto context = SDL_GL_CreateContext(window); - #ifndef MAGNUM_TARGET_GLES /* This must be enabled, otherwise (on my NVidia) it crashes when creating VAO. WTF. */ - glewExperimental = true; - - /* Init GLEW */ - GLenum err = glewInit(); - if(err != GLEW_OK) { - Error() << "Sdl2Context: cannot initialize GLEW:" << glewGetErrorString(err); - exit(1); - } - #endif + ExtensionWrangler::initialize(ExtensionWrangler::ExperimentalFeatures::Enable); /* Push resize event, so viewportEvent() is called at startup */ SDL_Event* sizeEvent = new SDL_Event; @@ -91,10 +83,10 @@ int Sdl2Context::exec() { keyReleaseEvent(static_cast(event.key.keysym.sym)); break; case SDL_MOUSEBUTTONDOWN: + mousePressEvent(static_cast(event.button.button), {event.button.x, event.button.y}); + break; case SDL_MOUSEBUTTONUP: - mouseEvent(static_cast(event.button.button), - static_cast(event.button.state), - {event.button.x, event.button.y}); + mouseReleaseEvent(static_cast(event.button.button), {event.button.x, event.button.y}); break; case SDL_MOUSEWHEEL: mouseWheelEvent({event.wheel.x, event.wheel.y}); @@ -109,8 +101,8 @@ int Sdl2Context::exec() { } if(_redraw) { - drawEvent(); _redraw = false; + drawEvent(); } else Corrade::Utility::sleep(5); } diff --git a/src/Contexts/Sdl2Context.h b/src/Contexts/Sdl2Context.h index fb4b00a22..112b434b8 100644 --- a/src/Contexts/Sdl2Context.h +++ b/src/Contexts/Sdl2Context.h @@ -79,6 +79,7 @@ class Sdl2Context: public AbstractContext { public: /** * @brief Key + * * @see keyPressEvent(), keyReleaseEvent() */ enum class Key: SDL_Keycode { @@ -111,6 +112,7 @@ class Sdl2Context: public AbstractContext { public: /** * @brief Mouse button + * * @see mouseEvent() */ enum class MouseButton: Uint8 { @@ -121,6 +123,7 @@ class Sdl2Context: public AbstractContext { /** * @brief Mouse state + * * @see mouseEvent() */ enum class MouseState: Uint8 { @@ -130,15 +133,24 @@ class Sdl2Context: public AbstractContext { protected: /** - * @brief Mouse event - * @param button Mouse button - * @param state Mouse state - * @param position Mouse position relative to the window + * @brief Mouse press event + * @param button Button pressed + * @param position Cursor position + * + * Called when mouse button is pressed. Default implementation does + * nothing. + */ + virtual void mousePressEvent(MouseButton button, const Math::Vector2& position); + + /** + * @brief Mouse release event + * @param button Button released + * @param position Cursor position * - * Called when mouse button is pressed or released. Default - * implementation does nothing. + * Called when mouse button is released. Default implementation does + * nothing. */ - virtual void mouseEvent(MouseButton button, MouseState state, const Math::Vector2& position); + virtual void mouseReleaseEvent(MouseButton button, const Math::Vector2& position); /** * @brief Mouse wheel event @@ -169,9 +181,10 @@ class Sdl2Context: public AbstractContext { }; /* Implementations for inline functions with unused parameters */ -inline void Sdl2Context::keyPressEvent(Sdl2Context::Key, Uint8) {} -inline void Sdl2Context::keyReleaseEvent(Sdl2Context::Key) {} -inline void Sdl2Context::mouseEvent(Sdl2Context::MouseButton, Sdl2Context::MouseState, const Math::Vector2&) {} +inline void Sdl2Context::keyPressEvent(Key, Uint8) {} +inline void Sdl2Context::keyReleaseEvent(Key) {} +inline void Sdl2Context::mousePressEvent(MouseButton, const Math::Vector2&) {} +inline void Sdl2Context::mouseReleaseEvent(MouseButton, const Math::Vector2&) {} inline void Sdl2Context::mouseWheelEvent(const Math::Vector2&) {} inline void Sdl2Context::mouseMotionEvent(const Math::Vector2&, const Math::Vector2&) {} diff --git a/src/Contexts/XEglContext.h b/src/Contexts/XEglContext.h new file mode 100644 index 000000000..fb368c1db --- /dev/null +++ b/src/Contexts/XEglContext.h @@ -0,0 +1,48 @@ +#ifndef Magnum_Contexts_XEglContext_h +#define Magnum_Contexts_XEglContext_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::Contexts::XEglContext + */ + +#include "AbstractXContext.h" +#include "EglInterface.h" + +namespace Magnum { namespace Contexts { + +/** +@brief X/EGL context + +Uses EglInterface. +*/ +class XEglContext: public AbstractXContext { + public: + /** + * @brief Constructor + * @param argc Count of arguments of `main()` function + * @param argv Arguments of `main()` function + * @param title Window title + * @param size Window size + * + * Creates window with double-buffered OpenGL ES 2 context. + */ + inline XEglContext(int& argc, char** argv, const std::string& title = "Magnum X/EGL context", const Math::Vector2& size = Math::Vector2(800, 600)): AbstractXContext(new EglInterface, argc, argv, title, size) {} +}; + +}} + +#endif diff --git a/src/CubeMapTexture.h b/src/CubeMapTexture.h index 7a8edbdfa..44ae71fce 100644 --- a/src/CubeMapTexture.h +++ b/src/CubeMapTexture.h @@ -23,7 +23,7 @@ namespace Magnum { -/** @ingroup textures +/** @brief Cube map texture %Texture used mainly for environemnt maps. See AbstractTexture documentation diff --git a/src/CubeMapTextureArray.h b/src/CubeMapTextureArray.h index 22d7e1cde..0a1f0ebca 100644 --- a/src/CubeMapTextureArray.h +++ b/src/CubeMapTextureArray.h @@ -23,7 +23,7 @@ namespace Magnum { -/** @ingroup textures +/** @brief Cube map texture array For information about usage, see CubeMapTexture documentation. diff --git a/src/Framebuffer.h b/src/Framebuffer.h index 27ee0cb85..427dcca32 100644 --- a/src/Framebuffer.h +++ b/src/Framebuffer.h @@ -19,7 +19,7 @@ * @brief Class Magnum::Framebuffer */ -#include +#include #include "BufferedImage.h" #include "CubeMapTexture.h" @@ -29,11 +29,12 @@ namespace Magnum { -/** @ingroup textures +/** @nosubgrouping @brief %Framebuffer Provides operations with framebuffers (configuring, clearing, blitting...) and creation and attaching of named framebuffers. +@todo @extension{ARB,viewport_array} */ class MAGNUM_EXPORT Framebuffer { Framebuffer(const Framebuffer& other) = delete; @@ -42,6 +43,8 @@ class MAGNUM_EXPORT Framebuffer { Framebuffer& operator=(Framebuffer&& other) = delete; public: + /** @{ @name Framebuffer features */ + /** * @brief Features * @@ -49,9 +52,20 @@ class MAGNUM_EXPORT Framebuffer { * @see setFeature() */ enum class Feature: GLenum { - AlphaBlending = GL_BLEND, /**< Alpha blending */ + /** + * Blending + * @see setBlendEquation(), setBlendFunction(), setBlendColor() + */ + Blending = GL_BLEND, #ifndef MAGNUM_TARGET_GLES + /** + * Logical operation + * @see setLogicOperation() + * @requires_gl Logic operations on framebuffer are in desktop OpenGL only. + */ + LogicOperation = GL_COLOR_LOGIC_OP, + /** * Depth clamping. If enabled, ignores near and far clipping plane. * @requires_gl @@ -60,12 +74,32 @@ class MAGNUM_EXPORT Framebuffer { DepthClamp = GL_DEPTH_CLAMP, #endif + /** + * Scissor test + * @see setScissor() + */ + ScissorTest = GL_SCISSOR_TEST, DepthTest = GL_DEPTH_TEST, /**< Depth test */ StencilTest = GL_STENCIL_TEST, /**< Stencil test */ Dithering = GL_DITHER, /**< Dithering (enabled by default) */ FaceCulling = GL_CULL_FACE /**< Back face culling */ }; + /** @brief Set feature */ + static void setFeature(Feature feature, bool enabled); + + /** + * @brief Set viewport size + * + * Call when window size changes. + * @see Camera::setViewport() + */ + static void setViewport(const Math::Vector2& position, const Math::Vector2& size); + + /*@}*/ + + /** @{ @name Clearing the framebuffer */ + /** * @brief Mask for clearing * @@ -77,289 +111,599 @@ class MAGNUM_EXPORT Framebuffer { Stencil = GL_STENCIL_BUFFER_BIT /**< Stencil value */ }; - typedef Corrade::Utility::Set ClearMask; /**< @brief Mask for clearing */ + typedef Corrade::Containers::EnumSet ClearMask; /**< @brief Mask for clearing */ /** - * @brief %Framebuffer target + * @brief Clear framebuffer * - * @see bind(), bindDefault() - * @requires_gl30 Extension @extension{EXT,framebuffer_object} + * Clears color buffer, depth and stencil buffer in currently active + * framebuffer. If depth or stencil test is not enabled, it doesn't + * clear these buffers. + * + * @see setFeature(), clear(ClearMask) */ - enum class Target: GLenum { - #ifndef MAGNUM_TARGET_GLES - /** - * For reading only. - * @requires_gl - * @requires_gl30 Extension @extension{EXT,framebuffer_blit} - */ - Read = GL_READ_FRAMEBUFFER, + inline static void clear() { glClear(static_cast(clearMask)); } - /** - * For drawing only. - * @requires_gl - * @requires_gl30 Extension @extension{EXT,framebuffer_blit} - */ - Draw = GL_DRAW_FRAMEBUFFER, - #endif + /** + * @brief Clear specified buffers in framebuffer + * + * @see clear() + * @todo Clearing only given draw buffer + */ + inline static void clear(ClearMask mask) { glClear(static_cast(mask)); } - ReadDraw = GL_FRAMEBUFFER /**< For both reading and drawing. */ - }; + /** + * @brief Set clear color + * + * Initial value is `{0.0f, 0.0f, 0.0f, 1.0f}`. + */ + inline static void setClearColor(const Color4& color) { + glClearColor(color.r(), color.g(), color.b(), color.a()); + } #ifndef MAGNUM_TARGET_GLES /** - * @brief Draw attachment for default framebuffer + * @brief Set clear depth * - * @see mapDefaultForDraw() - * @requires_gl - * @requires_gl30 Extension @extension{EXT,framebuffer_object} + * Initial value is `1.0`. + * @requires_gl See setClearDepth(GLfloat), which is supported in OpenGL ES. */ - enum class DefaultDrawAttachment: GLenum { - None = GL_NONE, /**< Don't use the output. */ - BackLeft = GL_BACK_LEFT, /**< Write output to back left framebuffer. */ - BackRight = GL_BACK_RIGHT, /**< Write output to back right framebuffer. */ - FrontLeft = GL_FRONT_LEFT, /**< Write output to front left framebuffer. */ - FrontRight = GL_FRONT_RIGHT /**< Write output to front right framebuffer. */ + inline static void setClearDepth(GLdouble depth) { glClearDepth(depth); } + #endif + + /** + * @overload + * + * @requires_gl41 Extension @extension{ARB,ES2_compatibility} + */ + inline static void setClearDepth(GLfloat depth) { glClearDepthf(depth); } + + /** + * @brief Set clear stencil + * + * Initial value is `0`. + */ + inline static void setClearStencil(GLint stencil) { glClearStencil(stencil); } + + /*@}*/ + + /** @{ @name Scissor operations */ + + /** + * @brief Set scissor rectangle + * @param bottomLeft Bottom left corner. Initial value is `(0, 0)`. + * @param size Scissor rectangle size. Initial value is + * size of the window when the context is first attached to a + * window. + * @attention You have to enable scissoring with setFeature() first. + */ + inline static void setScissor(const Math::Vector2& bottomLeft, const Math::Vector2& size) { + glScissor(bottomLeft.x(), bottomLeft.y(), size.x(), size.y()); + } + + /*@}*/ + + /** @{ @name Stencil operations */ + + /** + * @brief Affected polygon facing for stencil operations and masks + * + * @see setStencilFunction(PolygonFacing, StencilFunction, GLint, GLuint), + * setStencilOperation(PolygonFacing, StencilOperation, StencilOperation, StencilOperation), + * setStencilMask(PolygonFacing, GLuint) + */ + enum class PolygonFacing: GLenum { + Front = GL_FRONT, /**< Front-facing polygons */ + Back = GL_BACK, /**< Back-facing polygons */ + FrontAndBack = GL_FRONT_AND_BACK /**< Front- and back-facing polygons */ }; /** - * @brief Read attachment for default framebuffer + * @brief Stencil function * - * @see mapDefaultForRead() - * @requires_gl - * @requires_gl30 Extension @extension{EXT,framebuffer_object} + * @see setStencilFunction() */ - enum class DefaultReadAttachment: GLenum { - FrontLeft = GL_FRONT_LEFT, /**< Read from front left framebuffer. */ - FrontRight = GL_FRONT_RIGHT, /**< Read from front right framebuffer. */ - BackLeft = GL_BACK_LEFT, /**< Read from back left framebuffer. */ - BackRight = GL_BACK_RIGHT, /**< Read from back right framebuffer. */ - Left = GL_LEFT, /**< Read from left framebuffers. */ - Right = GL_RIGHT, /**< Read from right framebuffers. */ - Front = GL_FRONT, /**< Read from front framebuffers. */ - Back = GL_BACK, /**< Read from back framebuffers. */ - FrontAndBack = GL_FRONT_AND_BACK /**< Read from front and back framebuffers. */ + enum class StencilFunction: GLenum { + Never = GL_NEVER, /**< Never pass the test. */ + Always = GL_ALWAYS, /**< Always pass the test. */ + Less = GL_LESS, /**< Pass when reference value is less than buffer value. */ + LessOrEqual = GL_LEQUAL, /**< Pass when reference value is less than or equal to buffer value. */ + Equal = GL_EQUAL, /**< Pass when reference value is equal to buffer value. */ + NotEqual = GL_NOTEQUAL, /**< Pass when reference value is not equal to buffer value. */ + GreaterOrEqual = GL_GEQUAL, /**< Pass when reference value is greater than or equal to buffer value. */ + Greater = GL_GREATER /**< Pass when reference value is greater than buffer value. */ }; - #endif /** - * @brief Attachment for depth/stencil part of fragment shader output + * @brief Stencil operation * - * @see attachRenderbuffer(Target, DepthStencilAttachment, Renderbuffer*), - * attachTexture1D(Target, DepthStencilAttachment, Texture1D*, GLint), - * attachTexture2D(Target, DepthStencilAttachment, Texture2D*, GLint), - * attachCubeMapTexture(Target, DepthStencilAttachment, CubeMapTexture*, CubeMapTexture::Coordinate, GLint), - * attachTexture3D(Target, DepthStencilAttachment, Texture3D*, GLint) - * @requires_gl30 Extension @extension{EXT,framebuffer_object} + * @see setStencilOperation() */ - enum class DepthStencilAttachment: GLenum { - Depth = GL_DEPTH_ATTACHMENT, /**< Depth output only. */ + enum class StencilOperation: GLenum { + Keep = GL_KEEP, /**< Keep the current value. */ + Zero = GL_ZERO, /**< Set the stencil buffer value to `0`. */ - Stencil = GL_STENCIL_ATTACHMENT /**< Stencil output only. */ + /** + * Set the stencil value to reference value specified by + * setStencilFunction(). + */ + Replace = GL_REPLACE, - #ifndef MAGNUM_TARGET_GLES - , /** - * Both depth and stencil output. + * Increment the current stencil buffer value, clamp to maximum + * possible value on overflow. */ - DepthStencil = GL_DEPTH_STENCIL_ATTACHMENT - #endif + Increment = GL_INCR, + + /** + * Increment the current stencil buffer value, wrap to zero on + * overflow. + */ + IncrementWrap = GL_INCR_WRAP, + + /** + * Increment the current stencil buffer value, clamp to minimum + * possible value on underflow. + */ + Decrement = GL_DECR, + + /** + * Decrement the current stencil buffer value, wrap to maximum + * possible value on underflow. + */ + DecrementWrap = GL_DECR_WRAP, + + /** + * Bitwise invert the current stencil buffer value. + */ + Invert = GL_INVERT }; /** - * @brief Output mask for blitting + * @brief Set stencil function + * @param facing Affected polygon facing + * @param function Stencil function. Initial value is + * `StencilFunction::Always`. + * @param referenceValue Reference value. Initial value is `0`. + * @param mask Mask for both reference and buffer value. + * Initial value is all `1`s. * - * Specifies which data are copied when performing blit operation - * using blit(). - * @see BlitMask - * @requires_gl30 Extension @extension{EXT,framebuffer_object} + * @attention You have to enable stencil test with setFeature() first. + * @see setStencilFunction(StencilFunction, GLint, GLuint) */ - enum class Blit: GLbitfield { - Color = GL_COLOR_BUFFER_BIT, /**< Color */ - Depth = GL_DEPTH_BUFFER_BIT, /**< Depth value */ - Stencil = GL_STENCIL_BUFFER_BIT /**< Stencil value */ - }; + inline static void setStencilFunction(PolygonFacing facing, StencilFunction function, GLint referenceValue, GLuint mask) { + glStencilFuncSeparate(static_cast(facing), static_cast(function), referenceValue, mask); + } /** - * @brief Output mask for blitting - * @requires_gl30 Extension @extension{EXT,framebuffer_object} + * @brief Set stencil function + * + * The same as setStencilFunction(PolygonFacing, StencilFunction, GLint, GLuint) + * with `facing` set to `PolygonFacing::FrontAndBack`. */ - typedef Corrade::Utility::Set BlitMask; + inline static void setStencilFunction(StencilFunction function, GLint referenceValue, GLuint mask) { + glStencilFunc(static_cast(function), referenceValue, mask); + } - /** @brief Set feature */ - static void setFeature(Feature feature, bool enabled); + /** + * @brief Set stencil operation + * @param facing Affected polygon facing + * @param stencilFail Action when stencil test fails + * @param depthFail Action when stencil test passes, but depth + * test fails + * @param depthPass Action when both stencil and depth test + * pass + * + * Initial value for all fields is `StencilOperation::Keep`. + * @attention You have to enable stencil test with setFeature() first. + * @see setStencilOperation(StencilOperation, StencilOperation, StencilOperation) + */ + inline static void setStencilOperation(PolygonFacing facing, StencilOperation stencilFail, StencilOperation depthFail, StencilOperation depthPass) { + glStencilOpSeparate(static_cast(facing), static_cast(stencilFail), static_cast(depthFail), static_cast(depthPass)); + } /** - * @brief Set clear color + * @brief Set stencil operation * - * Initial value is `{0.0f, 0.0f, 0.0f, 1.0f}`. + * The same as setStencilOperation(PolygonFacing, StencilOperation, StencilOperation, StencilOperation) + * with `facing` set to `PolygonFacing::FrontAndBack`. */ - inline static void setClearColor(const Color4& color) { - glClearColor(color.r(), color.g(), color.b(), color.a()); + inline static void setStencilOperation(StencilOperation stencilFail, StencilOperation depthFail, StencilOperation depthPass) { + glStencilOp(static_cast(stencilFail), static_cast(depthFail), static_cast(depthPass)); } + /*@}*/ + + /** @{ @name Depth testing */ + /** - * @brief Clear framebuffer + * @brief Depth function * - * Clears color buffer, depth and stencil buffer in currently active - * framebuffer. If depth or stencil test is not enabled, it doesn't - * clear these buffers. + * @see setDepthFunction() + */ + typedef StencilFunction DepthFunction; + + /** + * @brief Set depth function * - * @see setFeature(), clear(ClearMask) + * Initial value is `DepthFunction::Less`. + * @attention You have to enable depth test with setFeature() first. */ - inline static void clear() { glClear(static_cast(clearMask)); } + inline static void setDepthFunction(DepthFunction function) { + glDepthFunc(static_cast(function)); + } + + /*@}*/ + + /** @{ @name Masking writes */ /** - * @brief Clear specified buffers in framebuffer + * @brief Mask color writes * - * @see clear() + * Set to `false` to disallow writing to given color channel. Initial + * values are all `true`. + * @todo Masking only given draw buffer */ - inline static void clear(ClearMask mask) { glClear(static_cast(mask)); } + inline static void setColorMask(GLboolean allowRed, GLboolean allowGreen, GLboolean allowBlue, GLboolean allowAlpha) { + glColorMask(allowRed, allowGreen, allowBlue, allowAlpha); + } - #ifndef MAGNUM_TARGET_GLES /** - * @brief Set clear depth + * @brief Mask depth writes * - * Initial value is `1.0`. - * @requires_gl See setClearDepth(GLfloat), which is supported in OpenGL ES. + * Set to `false` to disallow writing to depth buffer. Initial value + * is `true`. */ - inline static void setClearDepth(GLdouble depth) { glClearDepth(depth); } - #endif + inline static void setDepthMask(GLboolean allow) { + glDepthMask(allow); + } /** - * @overload + * @brief Mask stencil writes * - * @requires_gl41 Extension @extension{ARB,ES2_compatibility} + * Set given bit to `0` to disallow writing stencil value for given + * faces to it. Initial value is all `1`s. + * @see setStencilMask(GLuint) */ - inline static void setClearDepth(GLfloat depth) { glClearDepthf(depth); } + inline static void setStencilMask(PolygonFacing facing, GLuint allowBits) { + glStencilMaskSeparate(static_cast(facing), allowBits); + } /** - * @brief Set clear stencil + * @brief Mask stencil writes * - * Initial value is `0`. + * The same as setStencilMask(PolygonFacing, GLuint) with `facing` set + * to `PolygonFacing::FrontAndBack`. + */ + inline static void setStencilMask(GLuint allowBits) { + glStencilMask(allowBits); + } + + /*@}*/ + + /** @{ @name Blending + * You have to enable blending with setFeature() first. + * @todo Blending for given draw buffer */ - inline static void setClearStencil(GLint stencil) { glClearStencil(stencil); } /** - * @brief Set viewport size + * @brief Blend equation * - * Call when window size changes. - * @see Camera::setViewport() + * @see setBlendEquation() */ - static void setViewport(const Math::Vector2& position, const Math::Vector2& size); + enum class BlendEquation: GLenum { + Add = GL_FUNC_ADD, /**< `source + destination` */ + Subtract = GL_FUNC_SUBTRACT, /**< `source - destination` */ + ReverseSubtract = GL_FUNC_REVERSE_SUBTRACT /**< `destination - source` */ + + #ifndef MAGNUM_TARGET_GLES + /** @todo Enable for ES3 when the headers are available */ + , + + /** + * `min(source, destination)` + * @requires_gles30 Extension @es_extension{EXT,blend_minmax} + */ + Min = GL_MIN, + + /** + * `max(source, destination)` + * @requires_gles30 Extension @es_extension{EXT,blend_minmax} + */ + Max = GL_MAX + #endif + }; /** - * @brief Bind default framebuffer to given target - * @param target %Target + * @brief Blend function * - * @requires_gl30 Extension @extension{EXT,framebuffer_object} + * @see setBlendFunction() */ - inline static void bindDefault(Target target) { - glBindFramebuffer(static_cast(target), 0); + enum class BlendFunction: GLenum { + /** Zero (@f$ RGB = (0.0, 0.0, 0.0); A = 0.0 @f$) */ + Zero = GL_ZERO, + + /** One (@f$ RGB = (1.0, 1.0, 1.0); A = 1.0 @f$) */ + One = GL_ONE, + + /** + * Constant color (@f$ RGB = (R_c, G_c, B_c); A = A_c @f$) + * + * @see setBlendColor() + */ + ConstantColor = GL_CONSTANT_COLOR, + + /** + * One minus constant color (@f$ RGB = (1.0 - R_c, 1.0 - G_c, 1.0 - B_c); A = 1.0 - A_c @f$) + * + * @see setBlendColor() + */ + OneMinusConstantColor = GL_ONE_MINUS_CONSTANT_COLOR, + + /** + * Constant alpha (@f$ RGB = (A_c, A_c, A_c); A = A_c @f$) + * + * @see setBlendColor() + */ + ConstantAlpha = GL_CONSTANT_ALPHA, + + /** + * One minus constant alpha (@f$ RGB = (1.0 - A_c, 1.0 - A_c, 1.0 - A_c); A = 1.0 - A_c @f$) + * + * @see setBlendColor() + */ + OneMinusConstantAlpha = GL_ONE_MINUS_CONSTANT_ALPHA, + + /** Source color (@f$ RGB = (R_{s0}, G_{s0}, B_{s0}); A = A_{s0} @f$) */ + SourceColor = GL_SRC_COLOR, + + #ifndef MAGNUM_TARGET_GLES + /** + * Second source color (@f$ RGB = (R_{s1}, G_{s1}, B_{s1}); A = A_{s1} @f$) + * + * @see AbstractShaderProgram::bindFragmentDataLocationIndexed() + * @requires_gl + * @requires_gl33 Extension @extension{ARB,blend_func_extended} + */ + SecondSourceColor = GL_SRC1_COLOR, + #endif + + /** + * One minus source color (@f$ RGB = (1.0 - R_{s0}, 1.0 - G_{s0}, 1.0 - B_{s0}); A = 1.0 - A_{s0} @f$) + */ + OneMinusSourceColor = GL_ONE_MINUS_SRC_COLOR, + + #ifndef MAGNUM_TARGET_GLES + /** + * One minus second source color (@f$ RGB = (1.0 - R_{s1}, 1.0 - G_{s1}, 1.0 - B_{s1}); A = 1.0 - A_{s1} @f$) + * + * @see AbstractShaderProgram::bindFragmentDataLocationIndexed() + * @requires_gl + * @requires_gl33 Extension @extension{ARB,blend_func_extended} + */ + OneMinusSecondSourceColor = GL_ONE_MINUS_SRC1_COLOR, + #endif + + /** Source alpha (@f$ RGB = (A_{s0}, A_{s0}, A_{s0}); A = A_{s0} @f$) */ + SourceAlpha = GL_SRC_ALPHA, + + /** + * Saturate source alpha (@f$ RGB = (f, f, f); A = 1.0; f = min(A_s, 1.0 - A_d) @f$) + * + * Can be used only in source parameter of setBlendFunction(). + */ + SourceAlphaSaturate = GL_SRC_ALPHA_SATURATE, + + #ifndef MAGNUM_TARGET_GLES + /** + * Second source alpha (@f$ RGB = (A_{s1}, A_{s1}, A_{s1}); A = A_{s1} @f$) + * + * @see AbstractShaderProgram::bindFragmentDataLocationIndexed() + * @requires_gl + * @requires_gl33 Extension @extension{ARB,blend_func_extended} + */ + SecondSourceAlpha = GL_SRC1_ALPHA, + #endif + + /** + * One minus source alpha (@f$ RGB = (1.0 - A_{s0}, 1.0 - A_{s0}, 1.0 - A_{s0}); A = 1.0 - A_{s0} @f$) + */ + OneMinusSourceAlpha = GL_ONE_MINUS_SRC_ALPHA, + + #ifndef MAGNUM_TARGET_GLES + /** + * One minus second source alpha (@f$ RGB = (1.0 - A_{s1}, 1.0 - A_{s1}, 1.0 - A_{s1}); A = 1.0 - A_{s1} @f$) + * + * @see AbstractShaderProgram::bindFragmentDataLocationIndexed() + * @requires_gl + * @requires_gl33 Extension @extension{ARB,blend_func_extended} + */ + OneMinusSecondSourceAlpha = GL_ONE_MINUS_SRC1_ALPHA, + #endif + + /** Destination color (@f$ RGB = (R_d, G_d, B_d); A = A_d @f$) */ + DestinationColor = GL_DST_COLOR, + + /** + * One minus source color (@f$ RGB = (1.0 - R_d, 1.0 - G_d, 1.0 - B_d); A = 1.0 - A_d @f$) + */ + OneMinusDestinationColor = GL_ONE_MINUS_DST_COLOR, + + /** Destination alpha (@f$ RGB = (A_d, A_d, A_d); A = A_d @f$) */ + DestinationAlpha = GL_DST_ALPHA, + + /** + * One minus source alpha (@f$ RGB = (1.0 - A_d, 1.0 - A_d, 1.0 - A_d); A = 1.0 - A_d @f$) + */ + OneMinusDestinationAlpha = GL_ONE_MINUS_DST_ALPHA + }; + + /** + * @brief Set blend equation + * + * How to combine source color (pixel value) with destination color + * (framebuffer). Initial value is `BlendEquation::Add`. + * @attention You have to enable blending with setFeature() first. + * @see setBlendEquation(BlendEquation, BlendEquation) + */ + inline static void setBlendEquation(BlendEquation equation) { + glBlendEquation(static_cast(equation)); } - #ifndef MAGNUM_TARGET_GLES /** - * @brief Map given attachments of default framebuffer for drawing - * @param attachments Default attachments. If any value is - * DefaultAttachment::None, given output is not used. + * @brief Set blend equation separately for RGB and alpha components * - * If used for mapping output of fragment shader, the order must be as - * specified by the shader (see AbstractShaderProgram documentation). - * If used for blit(), the order is not important. Each used attachment - * should have either renderbuffer or texture attached for writing to - * work properly. - * @see mapForDraw(), mapDefaultForRead() - * @requires_gl - * @requires_gl30 Extension @extension{EXT,framebuffer_object} + * See setBlendEquation(BlendEquation) for more information. + * @attention You have to enable blending with setFeature() first. */ - static void mapDefaultForDraw(std::initializer_list attachments); + inline static void setBlendEquation(BlendEquation rgb, BlendEquation alpha) { + glBlendEquationSeparate(static_cast(rgb), static_cast(alpha)); + } /** - * @brief Map given attachment of default framebuffer for reading - * @param attachment Default attachment + * @brief Set blend function + * @param source How the source blending factor is computed + * from pixel value. Initial value is `BlendFunction::One`. + * @param destination How the destination blending factor is + * computed from framebuffer. Initial value is + * `BlendFunction::Zero`. * - * Each used attachment should have either renderbuffer or texture - * attached to work properly. - * @see mapForRead(), mapDefaultForDraw() - * @requires_gl - * @requires_gl30 Extension @extension{EXT,framebuffer_object} + * @attention You have to enable blending with setFeature() first. + * @see setBlendFunction(BlendFunction, BlendFunction, BlendFunction, BlendFunction) */ - inline static void mapDefaultForRead(DefaultReadAttachment attachment) { - bindDefault(Target::Read); - glReadBuffer(static_cast(attachment)); + inline static void setBlendFunction(BlendFunction source, BlendFunction destination) { + glBlendFunc(static_cast(source), static_cast(destination)); } - #endif - #ifndef MAGNUM_TARGET_GLES /** - * @brief Copy block of pixels from read to draw framebuffer - * @param bottomLeft Bottom left coordinates of source rectangle - * @param topRight Top right coordinates of source rectangle - * @param destinationBottomLeft Bottom left coordinates of destination rectangle - * @param destinationTopRight Top right coordinates of destination - * rectangle - * @param blitMask Blit mask - * @param filter Interpolation applied if the image is - * stretched + * @brief Set blend function separately for RGB and alpha components + * + * See setBlendFunction(BlendFunction, BlendFunction) for more information. + * @attention You have to enable blending with setFeature() first. + */ + inline static void setBlendFunction(BlendFunction sourceRgb, BlendFunction destinationRgb, BlendFunction sourceAlpha, BlendFunction destinationAlpha) { + glBlendFuncSeparate(static_cast(sourceRgb), static_cast(destinationRgb), static_cast(sourceAlpha), static_cast(destinationAlpha)); + } + + /** + * @brief Set blend color * - * See mapForRead() / mapDefaultForRead() and mapForDraw() / - * mapDefaultForDraw() for binding particular framebuffer for reading - * and drawing. If multiple attachments are specified in mapForDraw() - * / mapDefaultForDraw(), the data are written to each of them. - * @requires_gl - * @requires_gl30 Extension @extension{EXT,framebuffer_blit} + * Sets constant color used in setBlendFunction() by + * `BlendFunction::ConstantColor`, + * `BlendFunction::OneMinusConstantColor`, + * `BlendFunction::ConstantAlpha` and + * `BlendFunction::OneMinusConstantAlpha`. + * @attention You have to enable blending with setFeature() first. */ - inline static void blit(const Math::Vector2& bottomLeft, const Math::Vector2& topRight, const Math::Vector2& destinationBottomLeft, const Math::Vector2& destinationTopRight, BlitMask blitMask, AbstractTexture::Filter filter) { - glBlitFramebuffer(bottomLeft.x(), bottomLeft.y(), topRight.x(), topRight.y(), destinationBottomLeft.x(), destinationBottomLeft.y(), destinationTopRight.x(), destinationTopRight.y(), static_cast(blitMask), static_cast(filter)); + inline static void setBlendColor(const Color4& color) { + glBlendColor(color.r(), color.g(), color.b(), color.a()); } + /*@}*/ + + #ifndef MAGNUM_TARGET_GLES + /** @{ @name Logical operation */ + /** - * @brief Copy block of pixels from read to draw framebuffer - * @param bottomLeft Bottom left coordinates of source and - * destination rectangle - * @param topRight Top right coordinates of source and - * destination rectangle - * @param blitMask Blit mask + * @brief Logical operation * - * Convenience function when source rectangle is the same as - * destination rectangle. As the image is copied pixel-by-pixel, - * no interpolation is needed and thus - * AbstractTexture::Filter::NearestNeighbor filtering is used by - * default. + * @see setLogicOperation() * @requires_gl - * @requires_gl30 Extension @extension{EXT,framebuffer_blit} */ - inline static void blit(const Math::Vector2& bottomLeft, const Math::Vector2& topRight, BlitMask blitMask) { - glBlitFramebuffer(bottomLeft.x(), bottomLeft.y(), topRight.x(), topRight.y(), bottomLeft.x(), bottomLeft.y(), topRight.x(), topRight.y(), static_cast(blitMask), static_cast(AbstractTexture::Filter::NearestNeighbor)); + enum class LogicOperation: GLenum { + Clear = GL_CLEAR, /**< `0` */ + Set = GL_SET, /**< `1` */ + Copy = GL_COPY, /**< `source` */ + CopyInverted = GL_COPY_INVERTED,/**< `~source` */ + Noop = GL_NOOP, /**< `destination` */ + Invert = GL_INVERT, /**< `~destination` */ + And = GL_AND, /**< `source & destination` */ + AndReverse = GL_AND_REVERSE, /**< `source & ~destination` */ + AndInverted = GL_AND_INVERTED, /**< `~source & destination` */ + Nand = GL_NAND, /**< `~(source & destination)` */ + Or = GL_OR, /**< `source | destination` */ + OrReverse = GL_OR_REVERSE, /**< `source | ~destination` */ + OrInverted = GL_OR_INVERTED, /**< `~source | destination` */ + Nor = GL_NOR, /**< `~(source | destination)` */ + Xor = GL_XOR, /**< `source ^ destination` */ + Equivalence = GL_EQUIV /**< `~(source ^ destination)` */ + }; + + /** + * @brief Set logical operation + * + * @attention You have to enable logical operation with setFeature() first. + * @requires_gl Logic operations on framebuffer are in desktop OpenGL only. + */ + inline static void setLogicOperation(LogicOperation operation) { + glLogicOp(static_cast(operation)); } + + /*@}*/ #endif + /** @{ @name Framebuffer creation and binding */ + /** - * @brief Read block of pixels from framebuffer to image - * @param offset Offset in the framebuffer - * @param dimensions Image dimensions - * @param components Color components - * @param type Data type - * @param image %Image where to put the data + * @brief %Framebuffer target * + * @see bind(), bindDefault() * @requires_gl30 Extension @extension{EXT,framebuffer_object} */ - static void read(const Math::Vector2& offset, const Math::Vector2& dimensions, AbstractImage::Components components, AbstractImage::ComponentType type, Image2D* image); + enum class Target: GLenum { + #ifndef MAGNUM_TARGET_GLES + /** + * For reading only. + * @requires_gl + * @requires_gl30 Extension @extension{EXT,framebuffer_blit} + */ + Read = GL_READ_FRAMEBUFFER, + + /** + * For drawing only. + * @requires_gl + * @requires_gl30 Extension @extension{EXT,framebuffer_blit} + */ + Draw = GL_DRAW_FRAMEBUFFER, + #endif + + ReadDraw = GL_FRAMEBUFFER /**< For both reading and drawing. */ + }; #ifndef MAGNUM_TARGET_GLES /** - * @brief Read block of pixels from framebuffer to buffered image - * @param offset Offset in the framebuffer - * @param dimensions Image dimensions - * @param components Color components - * @param type Data type - * @param image Buffered image where to put the data - * @param usage %Buffer usage + * @brief Draw attachment for default framebuffer * + * @see mapDefaultForDraw() * @requires_gl * @requires_gl30 Extension @extension{EXT,framebuffer_object} */ - static void read(const Math::Vector2& offset, const Math::Vector2& dimensions, AbstractImage::Components components, AbstractImage::ComponentType type, BufferedImage2D* image, Buffer::Usage usage); + enum class DefaultDrawAttachment: GLenum { + None = GL_NONE, /**< Don't use the output. */ + BackLeft = GL_BACK_LEFT, /**< Write output to back left framebuffer. */ + BackRight = GL_BACK_RIGHT, /**< Write output to back right framebuffer. */ + FrontLeft = GL_FRONT_LEFT, /**< Write output to front left framebuffer. */ + FrontRight = GL_FRONT_RIGHT /**< Write output to front right framebuffer. */ + }; + + /** + * @brief Read attachment for default framebuffer + * + * @see mapDefaultForRead() + * @requires_gl + * @requires_gl30 Extension @extension{EXT,framebuffer_object} + */ + enum class DefaultReadAttachment: GLenum { + FrontLeft = GL_FRONT_LEFT, /**< Read from front left framebuffer. */ + FrontRight = GL_FRONT_RIGHT, /**< Read from front right framebuffer. */ + BackLeft = GL_BACK_LEFT, /**< Read from back left framebuffer. */ + BackRight = GL_BACK_RIGHT, /**< Read from back right framebuffer. */ + Left = GL_LEFT, /**< Read from left framebuffers. */ + Right = GL_RIGHT, /**< Read from right framebuffers. */ + Front = GL_FRONT, /**< Read from front framebuffers. */ + Back = GL_BACK, /**< Read from back framebuffers. */ + FrontAndBack = GL_FRONT_AND_BACK /**< Read from front and back framebuffers. */ + }; #endif /** @@ -378,6 +722,16 @@ class MAGNUM_EXPORT Framebuffer { */ inline ~Framebuffer() { glDeleteFramebuffers(1, &framebuffer); } + /** + * @brief Bind default framebuffer to given target + * @param target %Target + * + * @requires_gl30 Extension @extension{EXT,framebuffer_object} + */ + inline static void bindDefault(Target target) { + glBindFramebuffer(static_cast(target), 0); + } + /** * @brief Bind framebuffer * @@ -388,6 +742,22 @@ class MAGNUM_EXPORT Framebuffer { } #ifndef MAGNUM_TARGET_GLES + /** + * @brief Map given attachments of default framebuffer for drawing + * @param attachments Default attachments. If any value is + * DefaultAttachment::None, given output is not used. + * + * If used for mapping output of fragment shader, the order must be as + * specified by the shader (see AbstractShaderProgram documentation). + * If used for blit(), the order is not important. Each used attachment + * should have either renderbuffer or texture attached for writing to + * work properly. + * @see mapForDraw(), mapDefaultForRead() + * @requires_gl + * @requires_gl30 Extension @extension{EXT,framebuffer_object} + */ + static void mapDefaultForDraw(std::initializer_list attachments); + /** * @brief Map given color attachments of current framebuffer for drawing * @param colorAttachments Color attachment IDs. If any value is -1, @@ -404,6 +774,21 @@ class MAGNUM_EXPORT Framebuffer { */ void mapForDraw(std::initializer_list colorAttachments); + /** + * @brief Map given attachment of default framebuffer for reading + * @param attachment Default attachment + * + * Each used attachment should have either renderbuffer or texture + * attached to work properly. + * @see mapForRead(), mapDefaultForDraw() + * @requires_gl + * @requires_gl30 Extension @extension{EXT,framebuffer_object} + */ + inline static void mapDefaultForRead(DefaultReadAttachment attachment) { + bindDefault(Target::Read); + glReadBuffer(static_cast(attachment)); + } + /** * @brief Map given color attachment of current framebuffer for reading * @param colorAttachment Color attachment ID @@ -420,6 +805,34 @@ class MAGNUM_EXPORT Framebuffer { } #endif + /*@}*/ + + /** @{ @name Attaching textures and renderbuffers */ + + /** + * @brief Attachment for depth/stencil part of fragment shader output + * + * @see attachRenderbuffer(Target, DepthStencilAttachment, Renderbuffer*), + * attachTexture1D(Target, DepthStencilAttachment, Texture1D*, GLint), + * attachTexture2D(Target, DepthStencilAttachment, Texture2D*, GLint), + * attachCubeMapTexture(Target, DepthStencilAttachment, CubeMapTexture*, CubeMapTexture::Coordinate, GLint), + * attachTexture3D(Target, DepthStencilAttachment, Texture3D*, GLint, GLint) + * @requires_gl30 Extension @extension{EXT,framebuffer_object} + */ + enum class DepthStencilAttachment: GLenum { + Depth = GL_DEPTH_ATTACHMENT, /**< Depth output only. */ + + Stencil = GL_STENCIL_ATTACHMENT /**< Stencil output only. */ + + #ifndef MAGNUM_TARGET_GLES + , + /** + * Both depth and stencil output. + */ + DepthStencil = GL_DEPTH_STENCIL_ATTACHMENT + #endif + }; + /** * @brief Attach renderbuffer to given framebuffer depth/stencil attachment * @param target %Target @@ -592,12 +1005,115 @@ class MAGNUM_EXPORT Framebuffer { } #endif + /*@}*/ + + /** @{ @name Framebuffer blitting and reading */ + + #ifndef MAGNUM_TARGET_GLES + /** + * @brief Output mask for blitting + * + * Specifies which data are copied when performing blit operation + * using blit(). + * @see BlitMask + * @requires_gl + * @requires_gl30 Extension @extension{EXT,framebuffer_object} + */ + enum class Blit: GLbitfield { + Color = GL_COLOR_BUFFER_BIT, /**< Color */ + Depth = GL_DEPTH_BUFFER_BIT, /**< Depth value */ + Stencil = GL_STENCIL_BUFFER_BIT /**< Stencil value */ + }; + + /** + * @brief Output mask for blitting + * @requires_gl + * @requires_gl30 Extension @extension{EXT,framebuffer_object} + */ + typedef Corrade::Containers::EnumSet BlitMask; + + /** + * @brief Copy block of pixels from read to draw framebuffer + * @param bottomLeft Bottom left coordinates of source rectangle + * @param topRight Top right coordinates of source rectangle + * @param destinationBottomLeft Bottom left coordinates of destination rectangle + * @param destinationTopRight Top right coordinates of destination + * rectangle + * @param blitMask Blit mask + * @param filter Interpolation applied if the image is + * stretched + * + * See mapForRead() / mapDefaultForRead() and mapForDraw() / + * mapDefaultForDraw() for binding particular framebuffer for reading + * and drawing. If multiple attachments are specified in mapForDraw() + * / mapDefaultForDraw(), the data are written to each of them. + * @requires_gl + * @requires_gl30 Extension @extension{EXT,framebuffer_blit} + */ + inline static void blit(const Math::Vector2& bottomLeft, const Math::Vector2& topRight, const Math::Vector2& destinationBottomLeft, const Math::Vector2& destinationTopRight, BlitMask blitMask, AbstractTexture::Filter filter) { + glBlitFramebuffer(bottomLeft.x(), bottomLeft.y(), topRight.x(), topRight.y(), destinationBottomLeft.x(), destinationBottomLeft.y(), destinationTopRight.x(), destinationTopRight.y(), static_cast(blitMask), static_cast(filter)); + } + + /** + * @brief Copy block of pixels from read to draw framebuffer + * @param bottomLeft Bottom left coordinates of source and + * destination rectangle + * @param topRight Top right coordinates of source and + * destination rectangle + * @param blitMask Blit mask + * + * Convenience function when source rectangle is the same as + * destination rectangle. As the image is copied pixel-by-pixel, + * no interpolation is needed and thus + * AbstractTexture::Filter::NearestNeighbor filtering is used by + * default. + * @requires_gl + * @requires_gl30 Extension @extension{EXT,framebuffer_blit} + */ + inline static void blit(const Math::Vector2& bottomLeft, const Math::Vector2& topRight, BlitMask blitMask) { + glBlitFramebuffer(bottomLeft.x(), bottomLeft.y(), topRight.x(), topRight.y(), bottomLeft.x(), bottomLeft.y(), topRight.x(), topRight.y(), static_cast(blitMask), static_cast(AbstractTexture::Filter::NearestNeighbor)); + } + #endif + + /** + * @brief Read block of pixels from framebuffer to image + * @param offset Offset in the framebuffer + * @param dimensions %Image dimensions + * @param components Color components + * @param type Data type + * @param image %Image where to put the data + * + * @requires_gl30 Extension @extension{EXT,framebuffer_object} + */ + static void read(const Math::Vector2& offset, const Math::Vector2& dimensions, AbstractImage::Components components, AbstractImage::ComponentType type, Image2D* image); + + #ifndef MAGNUM_TARGET_GLES + /** + * @brief Read block of pixels from framebuffer to buffered image + * @param offset Offset in the framebuffer + * @param dimensions %Image dimensions + * @param components Color components + * @param type Data type + * @param image Buffered image where to put the data + * @param usage %Buffer usage + * + * @requires_gl + * @requires_gl30 Extension @extension{EXT,framebuffer_object} + */ + static void read(const Math::Vector2& offset, const Math::Vector2& dimensions, AbstractImage::Components components, AbstractImage::ComponentType type, BufferedImage2D* image, Buffer::Usage usage); + #endif + + /*@}*/ + private: static ClearMask clearMask; GLuint framebuffer; }; +CORRADE_ENUMSET_OPERATORS(Framebuffer::ClearMask) +CORRADE_ENUMSET_OPERATORS(Framebuffer::BlitMask) + } #endif diff --git a/src/Image.h b/src/Image.h index 61dcf30a0..64557ea23 100644 --- a/src/Image.h +++ b/src/Image.h @@ -16,7 +16,7 @@ */ /** @file - * @brief Class Magnum::Image + * @brief Class Magnum::Image, typedef Magnum::Image1D, Magnum::Image2D, Magnum::Image3D */ #include "AbstractImage.h" @@ -24,10 +24,6 @@ namespace Magnum { -/** @addtogroup textures - * @{ - */ - /** @brief %Image @@ -128,8 +124,6 @@ typedef Image<2> Image2D; /** @brief Three-dimensional image */ typedef Image<3> Image3D; -/*@}*/ - } #endif diff --git a/src/ImageWrapper.h b/src/ImageWrapper.h index fef9a13c0..b4dc0e4e6 100644 --- a/src/ImageWrapper.h +++ b/src/ImageWrapper.h @@ -24,10 +24,6 @@ namespace Magnum { -/** @addtogroup textures - * @{ - */ - /** @brief %Image wrapper @@ -113,8 +109,6 @@ typedef ImageWrapper<2> ImageWrapper2D; /** @brief Three-dimensional image wrapper */ typedef ImageWrapper<3> ImageWrapper3D; -/*@}*/ - } #endif diff --git a/src/IndexedMesh.h b/src/IndexedMesh.h index a400d7f55..7250357b2 100644 --- a/src/IndexedMesh.h +++ b/src/IndexedMesh.h @@ -24,7 +24,7 @@ namespace Magnum { -/** @ingroup rendering mesh +/** * @brief Indexed mesh */ class MAGNUM_EXPORT IndexedMesh: public Mesh { diff --git a/src/Math/CMakeLists.txt b/src/Math/CMakeLists.txt index d12a6a16c..a0cc17b69 100644 --- a/src/Math/CMakeLists.txt +++ b/src/Math/CMakeLists.txt @@ -6,7 +6,6 @@ set(MagnumMath_HEADERS Matrix.h Matrix3.h Matrix4.h - Swizzle.h Vector.h Vector2.h Vector3.h diff --git a/src/Math/Math.h b/src/Math/Math.h index 7c3955bed..a90e680c7 100644 --- a/src/Math/Math.h +++ b/src/Math/Math.h @@ -51,14 +51,14 @@ template struct Constants { #ifndef DOXYGEN_GENERATING_OUTPUT template<> struct Constants { - static inline constexpr double pi() { return 3.14159265359; } - static inline constexpr double sqrt2() { return 1.41421356237; } - static inline constexpr double sqrt3() { return 1.73205080757; } + static inline constexpr double pi() { return 3.141592653589793; } + static inline constexpr double sqrt2() { return 1.414213562373095; } + static inline constexpr double sqrt3() { return 1.732050807568877; } }; template<> struct Constants { - static inline constexpr float pi() { return 3.14159265359f; } - static inline constexpr float sqrt2() { return 1.41421356237f; } - static inline constexpr float sqrt3() { return 1.73205080757f; } + static inline constexpr float pi() { return 3.141592654f; } + static inline constexpr float sqrt2() { return 1.414213562f; } + static inline constexpr float sqrt3() { return 1.732050808f; } }; namespace Implementation { @@ -105,6 +105,8 @@ float a = normalize('\127'); // b = 1.0f float b = normalize('\127'); @endcode + +@todo Signed normalization to [-1.0, 1.0] like in OpenGL? */ template inline constexpr typename std::enable_if::value && std::is_integral::value, FloatingPoint>::type normalize(Integral value) { return (FloatingPoint(value)-FloatingPoint(std::numeric_limits::min()))/ @@ -119,6 +121,8 @@ integral type. @note For best precision, `FloatingPoint` type should be always larger that resulting `Integral` type (e.g. `double` to `int`, `long double` to `long long`). + +@todo Signed normalization to [-1.0, 1.0] like in OpenGL? */ template inline constexpr typename std::enable_if::value && std::is_integral::value, Integral>::type denormalize(FloatingPoint value) { return std::numeric_limits::min() + @@ -127,17 +131,23 @@ template inline constexpr typename std::ena } /** - * @brief Angle in degrees - * - * Function to make angle entering less error-prone. Converts the value to - * radians at compile time. For example `deg(180.0f)` is converted to `3.14f`. +@brief Angle in degrees + +Function to make angle entering less error-prone. Converts the value to +radians at compile time. For example `deg(180.0f)` is converted to `3.14f`. + +Usable for entering e.g. rotation: +@code +Matrix4::rotation(deg(30.0f), Vector3::yAxis()); +@endcode +@see rad() */ template inline constexpr T deg(T value) { return value*Constants::pi()/180; } /** * @brief Angle in radians * - * See also deg(). + * See deg() for more information. */ template inline constexpr T rad(T value) { return value; } diff --git a/src/Math/MathTypeTraits.h b/src/Math/MathTypeTraits.h index b3d3edd3b..c42dbb84f 100644 --- a/src/Math/MathTypeTraits.h +++ b/src/Math/MathTypeTraits.h @@ -35,7 +35,7 @@ namespace Magnum { namespace Math { -/** @ingroup utility +/** @brief Traits class for numeric types Traits classes are usable for detecting type features at compile time without diff --git a/src/Math/Matrix.h b/src/Math/Matrix.h index 6ea0ed3c1..263185396 100644 --- a/src/Math/Matrix.h +++ b/src/Math/Matrix.h @@ -30,10 +30,11 @@ namespace Implementation { #endif /** - * @brief %Matrix - * - * @todo @c PERFORMANCE - loop unrolling for Matrix<3, T> and Matrix<4, T> - * @todo first col, then row (cache adjacency) +@brief %Matrix + +@configurationvalueref{Magnum::Math::Matrix} +@todo @c PERFORMANCE - loop unrolling for Matrix<3, T> and Matrix<4, T> +@todo first col, then row (cache adjacency) */ template class Matrix { static_assert(size != 0, "Matrix cannot have zero elements"); @@ -41,6 +42,9 @@ template class Matrix { friend class Matrix; /* for ij() */ public: + const static size_t Size = size; /**< @brief %Matrix size */ + typedef T Type; /**< @brief %Matrix data type */ + /** * @brief %Matrix from array * @return Reference to the data as if it was Matrix, thus doesn't @@ -122,7 +126,12 @@ template class Matrix { inline T* data() { return _data; } inline constexpr const T* data() const { return _data; } /**< @overload */ - /** @brief %Matrix column */ + /** + * @brief %Matrix column + * + * For accessing individual elements prefer to use operator(), as it + * is guaranteed to not involve unnecessary conversions. + */ inline Vector& operator[](size_t col) { return Vector::from(_data+col*size); } @@ -131,6 +140,20 @@ template class Matrix { return Vector::from(_data+col*size); } + /** + * @brief Element on given position + * + * Prefer this instead of using `[][]`. + * @see operator[] + */ + inline T& operator()(size_t col, size_t row) { + return _data[col*size+row]; + } + /** @overload */ + inline constexpr const T& operator()(size_t col, size_t row) const { + return _data[col*size+row]; + } + /** @brief Equality operator */ inline bool operator==(const Matrix& other) const { for(size_t i = 0; i != size*size; ++i) @@ -240,26 +263,11 @@ template class Matrix { return Matrix(first, next...); } - #ifdef MAGNUM_GCC45_COMPATIBILITY - protected: - #endif - /* Used internally instead of [][], because GCC does some heavy - optimalization in release mode which breaks it */ - inline T& operator()(size_t col, size_t row) { - return _data[col*size+row]; - } - inline constexpr const T& operator()(size_t col, size_t row) const { - return _data[col*size+row]; - } - - #ifdef MAGNUM_GCC45_COMPATIBILITY - private: - #endif T _data[size*size]; }; -/** @debugoperator{Matrix} */ -template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Math::Matrix& value) { +/** @debugoperator{Magnum::Math::Matrix} */ +template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Math::Matrix& value) { debug << "Matrix("; debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, false); for(size_t row = 0; row != size; ++row) { @@ -321,7 +329,7 @@ template class MatrixDeterminant { T out(0); for(size_t col = 0; col != size; ++col) - out += ((col & 1) ? -1 : 1)*m[col][0]*m.ij(col, 0).determinant(); + out += ((col & 1) ? -1 : 1)*m(col, 0)*m.ij(col, 0).determinant(); return out; } @@ -331,7 +339,7 @@ template class MatrixDeterminant<2, T> { public: /** @brief Functor */ inline constexpr T operator()(const Matrix<2, T>& m) { - return m[0][0]*m[1][1] - m[1][0]*m[0][1]; + return m(0, 0)*m(1, 1) - m(1, 0)*m(0, 1); } }; @@ -339,7 +347,7 @@ template class MatrixDeterminant<1, T> { public: /** @brief Functor */ inline constexpr T operator()(const Matrix<1, T>& m) { - return m[0][0]; + return m(0, 0); } }; @@ -348,4 +356,41 @@ template class MatrixDeterminant<1, T> { }} +namespace Corrade { namespace Utility { + +/** @configurationvalue{Magnum::Math::Matrix} */ +template struct ConfigurationValue> { + /** @brief Writes elements separated with spaces */ + static std::string toString(const Magnum::Math::Matrix& value, int flags = 0) { + std::string output; + + for(size_t row = 0; row != size; ++row) { + for(size_t col = 0; col != size; ++col) { + if(!output.empty()) output += ' '; + output += ConfigurationValue::toString(value(col, row), flags); + } + } + + return output; + } + + /** @brief Reads elements separated with whitespace */ + static Magnum::Math::Matrix fromString(const std::string& stringValue, int flags = 0) { + Magnum::Math::Matrix result(Magnum::Math::Matrix::Zero); + std::istringstream in(stringValue); + + for(size_t row = 0; row != size; ++row) { + for(size_t col = 0; col != size; ++col) { + std::string num; + in >> num; + result(col, row) = ConfigurationValue::fromString(num, flags); + } + } + + return result; + } +}; + +}} + #endif diff --git a/src/Math/Matrix3.h b/src/Math/Matrix3.h index 1dec2c7f3..1cf5640b8 100644 --- a/src/Math/Matrix3.h +++ b/src/Math/Matrix3.h @@ -29,6 +29,7 @@ namespace Magnum { namespace Math { Provides functions for transformations in 2D. See also Matrix4 for 3D transformations. +@configurationvalueref{Magnum::Math::Matrix3} */ template class Matrix3: public Matrix<3, T> { public: @@ -36,7 +37,7 @@ template class Matrix3: public Matrix<3, T> { * @brief 2D translation matrix * @param vec Translation vector * - * @see Matrix4::translation() + * @see Matrix4::translation(), Vector2::xAxis(), Vector2::yAxis() */ inline constexpr static Matrix3 translation(const Vector2& vec) { return Matrix3( /* Column-major! */ @@ -50,7 +51,7 @@ template class Matrix3: public Matrix<3, T> { * @brief 2D scaling matrix * @param vec Scaling vector * - * @see Matrix4::scaling() + * @see Matrix4::scaling(), Vector2::xScale(), Vector2::yScale() */ inline constexpr static Matrix3 scaling(const Vector2& vec) { return Matrix3( /* Column-major! */ @@ -64,7 +65,7 @@ template class Matrix3: public Matrix<3, T> { * @brief 3D rotation matrix * @param angle Rotation angle (counterclockwise, in radians) * - * @see Matrix4::rotation() + * @see Matrix4::rotation(), deg(), rad() */ static Matrix3 rotation(T angle) { return Matrix3( /* Column-major! */ @@ -97,11 +98,16 @@ template class Matrix3: public Matrix<3, T> { MAGNUM_MATRIX_SUBCLASS_IMPLEMENTATION(Matrix3, Vector3, 3) }; -/** @debugoperator{Matrix3} */ +/** @debugoperator{Magnum::Math::Matrix3} */ template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Math::Matrix3& value) { return debug << static_cast&>(value); } }} +namespace Corrade { namespace Utility { + /** @configurationvalue{Magnum::Math::Matrix3} */ + template struct ConfigurationValue>: public ConfigurationValue> {}; +}} + #endif diff --git a/src/Math/Matrix4.h b/src/Math/Matrix4.h index 2f127d2bc..a1c558c72 100644 --- a/src/Math/Matrix4.h +++ b/src/Math/Matrix4.h @@ -29,6 +29,7 @@ namespace Magnum { namespace Math { Provides functions for transformations in 3D. See also Matrix3 for 2D transformations. +@configurationvalueref{Magnum::Math::Matrix4} @todo Shearing @todo Reflection */ @@ -38,7 +39,7 @@ template class Matrix4: public Matrix<4, T> { * @brief 3D translation matrix * @param vec Translation vector * - * @see Matrix3::translation() + * @see Matrix3::translation(), Vector3::xAxis(), Vector3::yAxis(), Vector3::zAxis() */ inline constexpr static Matrix4 translation(const Vector3& vec) { return Matrix4( /* Column-major! */ @@ -53,7 +54,7 @@ template class Matrix4: public Matrix<4, T> { * @brief 3D scaling matrix * @param vec Scaling vector * - * @see Matrix3::scaling() + * @see Matrix3::scaling(), Vector3::xScale(), Vector3::yScale(), Vector3::zScale() */ inline constexpr static Matrix4 scaling(const Vector3& vec) { return Matrix4( /* Column-major! */ @@ -69,7 +70,7 @@ template class Matrix4: public Matrix<4, T> { * @param angle Rotation angle (counterclockwise, in radians) * @param vec Rotation vector * - * @see Matrix3::rotation() + * @see Matrix3::rotation(), Vector3::xAxis(), Vector3::yAxis(), Vector3::zAxis(), deg(), rad() * @todo optimize - Assume the vectors are normalized? */ static Matrix4 rotation(T angle, const Vector3& vec) { @@ -159,11 +160,16 @@ template class Matrix4: public Matrix<4, T> { MAGNUM_MATRIX_SUBCLASS_IMPLEMENTATION(Matrix4, Vector4, 4) }; -/** @debugoperator{Matrix4} */ +/** @debugoperator{Magnum::Math::Matrix4} */ template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Math::Matrix4& value) { return debug << static_cast&>(value); } }} +namespace Corrade { namespace Utility { + /** @configurationvalue{Magnum::Math::Matrix4} */ + template struct ConfigurationValue>: public ConfigurationValue> {}; +}} + #endif diff --git a/src/Math/Test/CMakeLists.txt b/src/Math/Test/CMakeLists.txt index 9e336887f..05365c153 100644 --- a/src/Math/Test/CMakeLists.txt +++ b/src/Math/Test/CMakeLists.txt @@ -6,8 +6,6 @@ corrade_add_test2(MathVector2Test Vector2Test.cpp) corrade_add_test2(MathVector3Test Vector3Test.cpp) corrade_add_test2(MathVector4Test Vector4Test.cpp) -corrade_add_test2(MathSwizzleTest SwizzleTest.cpp) - corrade_add_test2(MathMatrixTest MatrixTest.cpp) corrade_add_test2(MathMatrix3Test Matrix3Test.cpp) corrade_add_test2(MathMatrix4Test Matrix4Test.cpp) diff --git a/src/Math/Test/MathTest.cpp b/src/Math/Test/MathTest.cpp index db4d29676..9d76e9535 100644 --- a/src/Math/Test/MathTest.cpp +++ b/src/Math/Test/MathTest.cpp @@ -24,13 +24,22 @@ CORRADE_TEST_MAIN(Magnum::Math::Test::MathTest) namespace Magnum { namespace Math { namespace Test { MathTest::MathTest() { - addTests(&MathTest::degrad, + addTests(&MathTest::constants, + &MathTest::degrad, &MathTest::normalize, &MathTest::denormalize, &MathTest::pow, &MathTest::log); } +void MathTest::constants() { + CORRADE_COMPARE(Math::pow<2>(Constants::sqrt2()), 2.0f); + CORRADE_COMPARE(Math::pow<2>(Constants::sqrt3()), 3.0f); + + CORRADE_COMPARE(Math::pow<2>(Constants::sqrt2()), 2.0); + CORRADE_COMPARE(Math::pow<2>(Constants::sqrt3()), 3.0); +} + void MathTest::degrad() { CORRADE_COMPARE(deg(90.0), Constants::pi()/2); CORRADE_COMPARE(deg(90.0f), Constants::pi()/2); diff --git a/src/Math/Test/MathTest.h b/src/Math/Test/MathTest.h index b6dcba0c8..3bb32c5ea 100644 --- a/src/Math/Test/MathTest.h +++ b/src/Math/Test/MathTest.h @@ -23,6 +23,7 @@ class MathTest: public Corrade::TestSuite::Tester { public: MathTest(); + void constants(); void degrad(); void normalize(); void denormalize(); diff --git a/src/Math/Test/Matrix3Test.cpp b/src/Math/Test/Matrix3Test.cpp index dc01cb2f2..840e75c92 100644 --- a/src/Math/Test/Matrix3Test.cpp +++ b/src/Math/Test/Matrix3Test.cpp @@ -34,7 +34,8 @@ Matrix3Test::Matrix3Test() { &Matrix3Test::translation, &Matrix3Test::scaling, &Matrix3Test::rotation, - &Matrix3Test::debug); + &Matrix3Test::debug, + &Matrix3Test::configuration); } void Matrix3Test::constructIdentity() { @@ -103,4 +104,15 @@ void Matrix3Test::debug() { " 8, 7, 8)\n"); } +void Matrix3Test::configuration() { + Matrix3 m( + 5.0f, 8.0f, 4.0f, + 4.0f, 7.0f, 3.125f, + 4.0f, 5.0f, 9.55f + ); + string value("5 4 4 8 7 5 4 3.125 9.55"); + CORRADE_COMPARE(ConfigurationValue::toString(m), value); + CORRADE_COMPARE(ConfigurationValue::fromString(value), m); +} + }}} diff --git a/src/Math/Test/Matrix3Test.h b/src/Math/Test/Matrix3Test.h index 27544cd0e..2402fbc93 100644 --- a/src/Math/Test/Matrix3Test.h +++ b/src/Math/Test/Matrix3Test.h @@ -28,7 +28,9 @@ class Matrix3Test: public Corrade::TestSuite::Tester { void translation(); void scaling(); void rotation(); + void debug(); + void configuration(); }; }}} diff --git a/src/Math/Test/Matrix4Test.cpp b/src/Math/Test/Matrix4Test.cpp index 0d9b813cb..9cc253cc6 100644 --- a/src/Math/Test/Matrix4Test.cpp +++ b/src/Math/Test/Matrix4Test.cpp @@ -37,7 +37,8 @@ Matrix4Test::Matrix4Test() { &Matrix4Test::rotation, &Matrix4Test::rotationScalingPart, &Matrix4Test::rotationPart, - &Matrix4Test::debug); + &Matrix4Test::debug, + &Matrix4Test::configuration); } void Matrix4Test::constructIdentity() { @@ -144,4 +145,16 @@ void Matrix4Test::debug() { " 4, 3, 0, 9)\n"); } +void Matrix4Test::configuration() { + Matrix4 m( + 3.0f, 5.0f, 8.0f, 4.0f, + 4.0f, 4.0f, 7.0f, 3.125f, + 7.0f, -1.0f, 8.0f, 0.0f, + 9.0f, 4.0f, 5.0f, 9.55f + ); + string value("3 4 7 9 5 4 -1 4 8 7 8 5 4 3.125 0 9.55"); + CORRADE_COMPARE(ConfigurationValue::toString(m), value); + CORRADE_COMPARE(ConfigurationValue::fromString(value), m); +} + }}} diff --git a/src/Math/Test/Matrix4Test.h b/src/Math/Test/Matrix4Test.h index 5138e3a3a..a49cfddb1 100644 --- a/src/Math/Test/Matrix4Test.h +++ b/src/Math/Test/Matrix4Test.h @@ -32,6 +32,7 @@ class Matrix4Test: public Corrade::TestSuite::Tester { void rotationPart(); void debug(); + void configuration(); }; }}} diff --git a/src/Math/Test/MatrixTest.cpp b/src/Math/Test/MatrixTest.cpp index 8514f8779..d9e986c85 100644 --- a/src/Math/Test/MatrixTest.cpp +++ b/src/Math/Test/MatrixTest.cpp @@ -44,7 +44,8 @@ MatrixTest::MatrixTest() { &MatrixTest::ij, &MatrixTest::determinant, &MatrixTest::inverted, - &MatrixTest::debug); + &MatrixTest::debug, + &MatrixTest::configuration); } void MatrixTest::construct() { @@ -124,9 +125,10 @@ void MatrixTest::data() { m[3] = vector; m[2][1] = 1.0f; - m[1][2] = 1.5f; + m(1, 2) = 1.5f; - CORRADE_COMPARE(m[2][1], 1.0f); + CORRADE_COMPARE(m(2, 1), 1.0f); + CORRADE_COMPARE(m[1][2], 1.5f); CORRADE_COMPARE(m[3], vector); Matrix4 expected( @@ -142,20 +144,20 @@ void MatrixTest::data() { void MatrixTest::copy() { Matrix4 m1(Matrix4::Zero); - m1[2][3] = 1.0f; + m1(2, 3) = 1.0f; /* Copy */ Matrix4 m2(m1); Matrix4 m3; - m3[0][0] = 1.0f; /* this line is here so it's not optimized to Matrix4 m3(m1) */ + m3(0, 0) = 1.0f; /* this line is here so it's not optimized to Matrix4 m3(m1) */ m3 = m1; /* Change original */ - m1[3][2] = 1.0f; + m1(3, 2) = 1.0f; /* Verify the copy is the same as original */ Matrix4 original(Matrix4::Zero); - original[2][3] = 1.0f; + original(2, 3) = 1.0f; CORRADE_COMPARE(m2, original); CORRADE_COMPARE(m3, original); @@ -307,4 +309,16 @@ void MatrixTest::debug() { " 0, 0, 0, 1)\n"); } +void MatrixTest::configuration() { + Matrix4 m( + 3.0f, 5.0f, 8.0f, 4.0f, + 4.0f, 4.0f, 7.0f, 3.125f, + 7.0f, -1.0f, 8.0f, 0.0f, + 9.0f, 4.0f, 5.0f, 9.55f + ); + string value("3 4 7 9 5 4 -1 4 8 7 8 5 4 3.125 0 9.55"); + CORRADE_COMPARE(ConfigurationValue::toString(m), value); + CORRADE_COMPARE(ConfigurationValue::fromString(value), m); +} + }}} diff --git a/src/Math/Test/MatrixTest.h b/src/Math/Test/MatrixTest.h index 704c95ff3..bb4eab650 100644 --- a/src/Math/Test/MatrixTest.h +++ b/src/Math/Test/MatrixTest.h @@ -38,6 +38,7 @@ class MatrixTest: public Corrade::TestSuite::Tester { void inverted(); void debug(); + void configuration(); }; }}} diff --git a/src/Math/Test/Vector2Test.cpp b/src/Math/Test/Vector2Test.cpp index 636f19c72..a60a253d7 100644 --- a/src/Math/Test/Vector2Test.cpp +++ b/src/Math/Test/Vector2Test.cpp @@ -30,17 +30,37 @@ typedef Math::Vector2 Vector2; Vector2Test::Vector2Test() { addTests(&Vector2Test::construct, - &Vector2Test::debug); + &Vector2Test::axes, + &Vector2Test::scales, + &Vector2Test::debug, + &Vector2Test::configuration); } void Vector2Test::construct() { CORRADE_COMPARE(Vector2(1, 2), (Vector<2, float>(1.0f, 2.0f))); } +void Vector2Test::axes() { + CORRADE_COMPARE(Vector2::xAxis(5.0f), Vector2(5.0f, 0.0f)); + CORRADE_COMPARE(Vector2::yAxis(6.0f), Vector2(0.0f, 6.0f)); +} + +void Vector2Test::scales() { + CORRADE_COMPARE(Vector2::xScale(-5.0f), Vector2(-5.0f, 1.0f)); + CORRADE_COMPARE(Vector2::yScale(-0.2f), Vector2(1.0f, -0.2f)); +} + void Vector2Test::debug() { ostringstream o; Debug(&o) << Vector2(0.5f, 15.0f); CORRADE_COMPARE(o.str(), "Vector(0.5, 15)\n"); } +void Vector2Test::configuration() { + Vector2 vec(3.125f, 9.0f); + string value("3.125 9"); + CORRADE_COMPARE(ConfigurationValue::toString(vec), value); + CORRADE_COMPARE(ConfigurationValue::fromString(value), vec); +} + }}} diff --git a/src/Math/Test/Vector2Test.h b/src/Math/Test/Vector2Test.h index 39515fbf9..48bcc1b9f 100644 --- a/src/Math/Test/Vector2Test.h +++ b/src/Math/Test/Vector2Test.h @@ -24,7 +24,11 @@ class Vector2Test: public Corrade::TestSuite::Tester { Vector2Test(); void construct(); + void axes(); + void scales(); + void debug(); + void configuration(); }; }}} diff --git a/src/Math/Test/Vector3Test.cpp b/src/Math/Test/Vector3Test.cpp index 6df8a84e7..710c60cd0 100644 --- a/src/Math/Test/Vector3Test.cpp +++ b/src/Math/Test/Vector3Test.cpp @@ -32,9 +32,11 @@ typedef Math::Vector2 Vector2; Vector3Test::Vector3Test() { addTests(&Vector3Test::construct, &Vector3Test::cross, - &Vector3Test::axis, + &Vector3Test::axes, + &Vector3Test::scales, &Vector3Test::twoComponent, - &Vector3Test::debug); + &Vector3Test::debug, + &Vector3Test::configuration); } void Vector3Test::construct() { @@ -49,12 +51,18 @@ void Vector3Test::cross() { CORRADE_COMPARE(Vector3::cross(a, b), Vector3(-10, -3, 7)); } -void Vector3Test::axis() { +void Vector3Test::axes() { CORRADE_COMPARE(Vector3::xAxis(5.0f), Vector3(5.0f, 0.0f, 0.0f)); CORRADE_COMPARE(Vector3::yAxis(6.0f), Vector3(0.0f, 6.0f, 0.0f)); CORRADE_COMPARE(Vector3::zAxis(7.0f), Vector3(0.0f, 0.0f, 7.0f)); } +void Vector3Test::scales() { + CORRADE_COMPARE(Vector3::xScale(-5.0f), Vector3(-5.0f, 1.0f, 1.0f)); + CORRADE_COMPARE(Vector3::yScale(-0.2f), Vector3(1.0f, -0.2f, 1.0f)); + CORRADE_COMPARE(Vector3::zScale(71.0f), Vector3(1.0f, 1.0f, 71.0f)); +} + void Vector3Test::twoComponent() { CORRADE_COMPARE(Vector3(1.0f, 2.0f, 3.0f).xy(), Vector2(1.0f, 2.0f)); } @@ -65,4 +73,11 @@ void Vector3Test::debug() { CORRADE_COMPARE(o.str(), "Vector(0.5, 15, 1)\n"); } +void Vector3Test::configuration() { + Vector3 vec(3.0f, 3.125f, 9.55f); + string value("3 3.125 9.55"); + CORRADE_COMPARE(ConfigurationValue::toString(vec), value); + CORRADE_COMPARE(ConfigurationValue::fromString(value), vec); +} + }}} diff --git a/src/Math/Test/Vector3Test.h b/src/Math/Test/Vector3Test.h index 73de924e9..1542c6a06 100644 --- a/src/Math/Test/Vector3Test.h +++ b/src/Math/Test/Vector3Test.h @@ -25,10 +25,12 @@ class Vector3Test: public Corrade::TestSuite::Tester { void construct(); void cross(); - void axis(); + void axes(); + void scales(); void twoComponent(); void debug(); + void configuration(); }; }}} diff --git a/src/Math/Test/Vector4Test.cpp b/src/Math/Test/Vector4Test.cpp index 57a0b9b7d..b8765b65b 100644 --- a/src/Math/Test/Vector4Test.cpp +++ b/src/Math/Test/Vector4Test.cpp @@ -34,7 +34,8 @@ Vector4Test::Vector4Test() { addTests(&Vector4Test::construct, &Vector4Test::threeComponent, &Vector4Test::twoComponent, - &Vector4Test::debug); + &Vector4Test::debug, + &Vector4Test::configuration); } void Vector4Test::construct() { @@ -57,4 +58,11 @@ void Vector4Test::debug() { CORRADE_COMPARE(o.str(), "Vector(0.5, 15, 1, 1)\n"); } +void Vector4Test::configuration() { + Vector4 vec(3.0f, 3.125f, 9.0f, 9.55f); + string value("3 3.125 9 9.55"); + CORRADE_COMPARE(ConfigurationValue::toString(vec), value); + CORRADE_COMPARE(ConfigurationValue::fromString(value), vec); +} + }}} diff --git a/src/Math/Test/Vector4Test.h b/src/Math/Test/Vector4Test.h index d31df492a..e06ce248a 100644 --- a/src/Math/Test/Vector4Test.h +++ b/src/Math/Test/Vector4Test.h @@ -28,6 +28,7 @@ class Vector4Test: public Corrade::TestSuite::Tester { void twoComponent(); void debug(); + void configuration(); }; }}} diff --git a/src/Math/Test/VectorTest.cpp b/src/Math/Test/VectorTest.cpp index 327c2cb49..cbafca1c9 100644 --- a/src/Math/Test/VectorTest.cpp +++ b/src/Math/Test/VectorTest.cpp @@ -28,15 +28,18 @@ using namespace Corrade::Utility; namespace Magnum { namespace Math { namespace Test { typedef Vector<4, float> Vector4; +typedef Vector<4, int> Vector4i; typedef Vector<3, float> Vector3; VectorTest::VectorTest() { addTests(&VectorTest::construct, + &VectorTest::constructFrom, &VectorTest::data, &VectorTest::copy, &VectorTest::dot, &VectorTest::multiplyDivide, - &VectorTest::addSubstract, + &VectorTest::multiplyDivideComponentWise, + &VectorTest::addSubtract, &VectorTest::dotSelf, &VectorTest::length, &VectorTest::normalized, @@ -46,7 +49,8 @@ VectorTest::VectorTest() { &VectorTest::max, &VectorTest::angle, &VectorTest::negative, - &VectorTest::debug); + &VectorTest::debug, + &VectorTest::configuration); } void VectorTest::construct() { @@ -56,6 +60,15 @@ void VectorTest::construct() { CORRADE_COMPARE(Vector4::from(data), Vector4(1.0f, 2.0f, 3.0f, 4.0f)); } +void VectorTest::constructFrom() { + Vector4 floatingPoint(1.3f, 2.7f, -15.0f, 7.0f); + Vector4 floatingPointRounded(1.0f, 2.0f, -15.0f, 7.0f); + Vector4i integral(1, 2, -15, 7); + + CORRADE_COMPARE(Vector4i::from(floatingPoint), integral); + CORRADE_COMPARE(Vector4::from(integral), floatingPointRounded); +} + void VectorTest::data() { Vector4 v; v[2] = 1.5f; @@ -95,15 +108,32 @@ void VectorTest::multiplyDivide() { Vector4 multiplied(-1.5f, -3.0f, -4.5f, -6.0f); CORRADE_COMPARE(vec*-1.5f, multiplied); + CORRADE_COMPARE(-1.5f*vec, multiplied); CORRADE_COMPARE(multiplied/-1.5f, vec); Math::Vector<1, char> vecChar(32); Math::Vector<1, char> multipliedChar(-48); CORRADE_COMPARE(vecChar*-1.5f, multipliedChar); CORRADE_COMPARE(multipliedChar/-1.5f, vecChar); + CORRADE_COMPARE(-1.5f*vecChar, multipliedChar); + + /* Divide vector with number and inverse */ + Vector4 divisor(1.0f, 2.0f, -4.0f, 8.0f); + Vector4 result(1.0f, 0.5f, -0.25f, 0.125f); + CORRADE_COMPARE(1.0f/divisor, result); + CORRADE_COMPARE(-1550.0f/multipliedChar, vecChar); +} + +void VectorTest::multiplyDivideComponentWise() { + Vector4 vec(1.0f, 2.0f, 3.0f, 4.0f); + Vector4 multiplier(7.0f, -4.0f, -1.5f, 1.0f); + Vector4 multiplied(7.0f, -8.0f, -4.5f, 4.0f); + + CORRADE_COMPARE(vec*multiplier, multiplied); + CORRADE_COMPARE(multiplied/multiplier, vec); } -void VectorTest::addSubstract() { +void VectorTest::addSubtract() { Vector4 a(0.5f, -7.5f, 9.0f, -11.0f); Vector4 b(-0.5, 1.0f, 0.0f, 7.5f); Vector4 expected(0.0f, -6.5f, 9.0f, -3.5f); @@ -165,4 +195,11 @@ void VectorTest::debug() { CORRADE_COMPARE(o.str(), "a Vector(0, 0, 0, 0) b Vector(0, 0, 0, 0)\n"); } +void VectorTest::configuration() { + Vector4 vec(3.0f, 3.125f, 9.0f, 9.55f); + string value("3 3.125 9 9.55"); + CORRADE_COMPARE(ConfigurationValue::toString(vec), value); + CORRADE_COMPARE(ConfigurationValue::fromString(value), vec); +} + }}} diff --git a/src/Math/Test/VectorTest.h b/src/Math/Test/VectorTest.h index 662865119..cde0cafa9 100644 --- a/src/Math/Test/VectorTest.h +++ b/src/Math/Test/VectorTest.h @@ -24,11 +24,13 @@ class VectorTest: public Corrade::TestSuite::Tester { VectorTest(); void construct(); + void constructFrom(); void data(); void copy(); void dot(); void multiplyDivide(); - void addSubstract(); + void multiplyDivideComponentWise(); + void addSubtract(); void dotSelf(); void length(); void normalized(); @@ -40,6 +42,7 @@ class VectorTest: public Corrade::TestSuite::Tester { void negative(); void debug(); + void configuration(); }; }}} diff --git a/src/Math/Vector.h b/src/Math/Vector.h index bfdb1681f..c6344ac0b 100644 --- a/src/Math/Vector.h +++ b/src/Math/Vector.h @@ -22,11 +22,14 @@ #include #include #include +#include #include "MathTypeTraits.h" namespace Magnum { namespace Math { +template class Vector; + #ifndef DOXYGEN_GENERATING_OUTPUT namespace Implementation { template struct Sequence {}; @@ -38,10 +41,20 @@ namespace Implementation { template struct GenerateSequence<0, sequence...> { typedef Sequence Type; }; + + /* Implementation for Vector::from(const Vector&) */ + template inline constexpr Math::Vector vectorFrom(Sequence, const Math::Vector& vector) { + return {T(vector[sequence])...}; + } } #endif -/** @brief %Vector */ +/** +@brief %Vector + +@configurationvalueref{Magnum::Math::Vector} +@todo Constexprize all for loops +*/ template class Vector { static_assert(size != 0, "Vector cannot have zero elements"); @@ -65,6 +78,21 @@ template class Vector { return *reinterpret_cast*>(data); } + /** + * @brief %Vector from another of different type + * + * Performs only default casting on the values, no rounding or + * anything else. Example usage: + * @code + * Vector<4, float> floatingPoint(1.3f, 2.7f, -15.0f, 7.0f); + * Vector<4, int> integral(Vector<4, int>::from(floatingPoint)); + * // integral == {1, 2, -15, 7} + * @endcode + */ + template inline constexpr static Vector from(const Vector& other) { + return Implementation::vectorFrom(typename Implementation::GenerateSequence::Type(), other); + } + /** * @brief Dot product * @@ -161,45 +189,107 @@ template class Vector { /** * @brief Multiply vector * - * Note that corresponding operator with swapped type order - * (multiplying number with vector) is not available, because it would - * cause ambiguity in some cases. + * @see operator*=(U), operator*(U, const Vector&) */ + #ifndef DOXYGEN_GENERATING_OUTPUT + template inline typename std::enable_if::value, Vector>::type operator*(U number) const { + #else template inline Vector operator*(U number) const { + #endif return Vector(*this)*=number; } /** - * @brief Multiply and assign vector + * @brief Multiply vector and assign * - * More efficient than operator*(), because it does the computation - * in-place. + * More efficient than operator*(U) const, because it does the + * computation in-place. */ + #ifndef DOXYGEN_GENERATING_OUTPUT + template typename std::enable_if::value, Vector&>::type operator*=(U number) { + #else template Vector& operator*=(U number) { + #endif for(size_t i = 0; i != size; ++i) (*this)[i] *= number; return *this; } - /** @brief Divide vector */ + /** + * @brief Multiply vector component-wise + * + * @see operator*=(const Vector&) + */ + Vector operator*(const Vector& other) const { + return Vector(*this)*=other; + } + + /** + * @brief Multiply vector component-wise and assign + * + * More efficient than operator*(const Vector&) const, + * because it does the computation in-place. + */ + Vector& operator*=(const Vector& other) { + for(size_t i = 0; i != size; ++i) + (*this)[i] *= other[i]; + + return *this; + } + + /** + * @brief Divide vector + * + * @see operator/=(U), operator/(U, const Vector&) + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + template inline typename std::enable_if::value, Vector>::type operator/(U number) const { + #else template inline Vector operator/(U number) const { + #endif return Vector(*this)/=number; } /** - * @brief Divide and assign vector + * @brief Divide vector and assign * - * More efficient than operator/(), because it does the computation - * in-place. + * More efficient than operator/(U) const, because it does the + * computation in-place. */ + #ifndef DOXYGEN_GENERATING_OUTPUT + template typename std::enable_if::value, Vector&>::type operator/=(U number) { + #else template Vector& operator/=(U number) { + #endif for(size_t i = 0; i != size; ++i) (*this)[i] /= number; return *this; } + /** + * @brief Divide vector component-wise + * + * @see operator/=(const Vector&) + */ + Vector operator/(const Vector& other) const { + return Vector(*this)/=other; + } + + /** + * @brief Divide vector component-wise and assign + * + * More efficient than operator/(const Vector&) const, + * because it does the computation in-place. + */ + Vector& operator/=(const Vector& other) { + for(size_t i = 0; i != size; ++i) + (*this)[i] /= other[i]; + + return *this; + } + /** @brief Add two vectors */ inline Vector operator+(const Vector& other) const { return Vector(*this)+=other; @@ -218,13 +308,13 @@ template class Vector { return *this; } - /** @brief Substract two vectors */ + /** @brief Subtract two vectors */ inline Vector operator-(const Vector& other) const { return Vector(*this)-=other; } /** - * @brief Substract and assign vector + * @brief Subtract and assign vector * * More efficient than operator-(), because it does the computation * in-place. @@ -318,8 +408,44 @@ template class Vector { T _data[size]; }; -/** @debugoperator{Vector} */ -template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Math::Vector& value) { +/** @relates Vector +@brief Multiply number with vector + +@see Vector::operator*(U) const +*/ +#ifndef DOXYGEN_GENERATING_OUTPUT +template inline typename std::enable_if::value, Vector>::type operator*(U number, const Vector& vector) { +#else +template inline Vector operator*(U number, const Vector& vector) { +#endif + return vector*number; +} + +/** @relates Vector +@brief Divide vector with number and invert + +Example: +@code +Vector<4, float> vec(1.0f, 2.0f, -4.0f, 8.0f); +Vector<4, float> another = 1.0f/vec; // {1.0f, 0.5f, -0.25f, 0.128f} +@endcode +@see Vector::operator/(U) const +*/ +#ifndef DOXYGEN_GENERATING_OUTPUT +template typename std::enable_if::value, Vector>::type operator/(U number, const Vector& vector) { +#else +template Vector operator/(U number, const Vector& vector) { +#endif + Vector out; + + for(size_t i = 0; i != size; ++i) + out[i] = number/vector[i]; + + return out; +} + +/** @debugoperator{Magnum::Math::Vector} */ +template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Math::Vector& value) { debug << "Vector("; debug.setFlag(Corrade::Utility::Debug::SpaceAfterEachValue, false); for(size_t i = 0; i != size; ++i) { @@ -338,6 +464,9 @@ template Corrade::Utility::Debug operator<<(Corrade::Utili } \ inline constexpr static const Type& from(const T* data) { \ return *reinterpret_cast*>(data); \ + } \ + template inline constexpr static Type from(const Vector& other) { \ + return Vector::from(other); \ } \ \ inline Type& operator=(const Type& other) { \ @@ -352,12 +481,26 @@ template Corrade::Utility::Debug operator<<(Corrade::Utili Vector::operator*=(number); \ return *this; \ } \ + inline Type operator*(const Vector& other) const { \ + return Vector::operator*(other); \ + } \ + inline Type& operator*=(const Vector& other) { \ + Vector::operator*=(other); \ + return *this; \ + } \ template inline Type operator/(U number) const { \ return Vector::operator/(number); \ } \ template inline Type& operator/=(U number) { \ Vector::operator/=(number); \ return *this; \ + } \ + inline Type operator/(const Vector& other) const { \ + return Vector::operator/(other); \ + } \ + inline Type& operator/=(const Vector& other) { \ + Vector::operator/=(other); \ + return *this; \ } \ \ inline Type operator+(const Vector& other) const { \ @@ -377,8 +520,49 @@ template Corrade::Utility::Debug operator<<(Corrade::Utili \ inline Type operator-() const { return Vector::operator-(); } \ inline Type normalized() const { return Vector::normalized(); } + +#define MAGNUM_VECTOR_SUBCLASS_OPERATOR_IMPLEMENTATION(Type, size) \ + template inline Type operator*(U number, const Type& vector) { \ + return number*Vector(vector); \ + } \ + template inline Type operator/(U number, const Type& vector) { \ + return number/Vector(vector); \ + } #endif }} +namespace Corrade { namespace Utility { + +/** @configurationvalue{Magnum::Math::Vector} */ +template struct ConfigurationValue> { + /** @brief Writes elements separated with spaces */ + static std::string toString(const Magnum::Math::Vector& value, int flags = 0) { + std::string output; + + for(size_t pos = 0; pos != size; ++pos) { + if(!output.empty()) output += ' '; + output += ConfigurationValue::toString(value[pos], flags); + } + + return output; + } + + /** @brief Reads elements separated with whitespace */ + static Magnum::Math::Vector fromString(const std::string& stringValue, int flags = 0) { + Magnum::Math::Vector result; + std::istringstream in(stringValue); + + std::string num; + for(size_t pos = 0; pos != size; ++pos) { + in >> num; + result[pos] = ConfigurationValue::fromString(num, flags); + } + + return result; + } +}; + +}} + #endif diff --git a/src/Math/Vector2.h b/src/Math/Vector2.h index 5920261c4..68d19cb33 100644 --- a/src/Math/Vector2.h +++ b/src/Math/Vector2.h @@ -23,9 +23,51 @@ namespace Magnum { namespace Math { -/** @brief Two-component vector */ +/** +@brief Two-component vector + +@configurationvalueref{Magnum::Math::Vector2} +*/ template class Vector2: public Vector<2, T> { public: + /** + * @brief %Vector in direction of X axis + * + * Usable for translation in given axis, for example: + * @code + * Matrix3::translation(Vector2::xAxis(5.0f)); // same as Matrix3::translation({5.0f, 0.0f}); + * @endcode + * @see yAxis(), xScale() + */ + inline constexpr static Vector2 xAxis(T length = T(1)) { return Vector2(length, T()); } + + /** + * @brief %Vector in direction of Y axis + * + * See xAxis() for more information. + * @see yScale() + */ + inline constexpr static Vector2 yAxis(T length = T(1)) { return Vector2(T(), length); } + + /** + * @brief Scaling vector in direction of X axis + * + * Usable for scaling along given direction, for example: + * @code + * Matrix3::scaling(Vector2::xScale(-2.0f)); // same as Matrix3::scaling({-2.0f, 1.0f}); + * @endcode + * @see yScale(), xAxis() + */ + inline constexpr static Vector2 xScale(T scale) { return Vector2(scale, T(1)); } + + /** + * @brief Scaling vector in direction of Y axis + * + * See xScale() for more information. + * @see yAxis() + */ + inline constexpr static Vector2 yScale(T scale) { return Vector2(T(1), scale); } + /** @copydoc Vector::Vector(T) */ inline constexpr explicit Vector2(T value = T()): Vector<2, T>(value, value) {} @@ -48,11 +90,18 @@ template class Vector2: public Vector<2, T> { MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION(Vector2, 2) }; -/** @debugoperator{Vector2} */ +MAGNUM_VECTOR_SUBCLASS_OPERATOR_IMPLEMENTATION(Vector2, 2) + +/** @debugoperator{Magnum::Math::Vector2} */ template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Math::Vector2& value) { return debug << static_cast&>(value); } }} +namespace Corrade { namespace Utility { + /** @configurationvalue{Magnum::Math::Vector2} */ + template struct ConfigurationValue>: public ConfigurationValue> {}; +}} + #endif diff --git a/src/Math/Vector3.h b/src/Math/Vector3.h index d70e6dc08..212e79ac4 100644 --- a/src/Math/Vector3.h +++ b/src/Math/Vector3.h @@ -23,18 +23,68 @@ namespace Magnum { namespace Math { -/** @brief Three-component vector */ +/** +@brief Three-component vector + +@configurationvalueref{Magnum::Math::Vector3} +*/ template class Vector3: public Vector<3, T> { public: - /** @brief %Vector in direction of X axis */ + /** + * @brief %Vector in direction of X axis + * + * Usable for translation or rotation along given axis, for example: + * @code + * Matrix4::translation(Vector3::xAxis(5.0f)); // same as Matrix4::translation({5.0f, 0.0f, 0.0f}); + * Matrix4::rotation(deg(30.0f), Vector3::xAxis()); // same as Matrix::rotation(deg(30.0f), {1.0f, 0.0f, 0.0f}); + * @endcode + * @see yAxis(), zAxis(), xScale() + */ inline constexpr static Vector3 xAxis(T length = T(1)) { return Vector3(length, T(), T()); } - /** @brief %Vector in direction of Y axis */ + /** + * @brief %Vector in direction of Y axis + * + * See xAxis() for more information. + * @see yScale() + */ inline constexpr static Vector3 yAxis(T length = T(1)) { return Vector3(T(), length, T()); } - /** @brief %Vector in direction of Z axis */ + /** + * @brief %Vector in direction of Z axis + * + * See xAxis() for more information. + * @see zScale() + */ inline constexpr static Vector3 zAxis(T length = T(1)) { return Vector3(T(), T(), length); } + /** + * @brief Scaling vector in direction of X axis + * + * Usable for scaling along given direction, for example: + * @code + * Matrix4::scaling(Vector3::xScale(-2.0f)); // same as Matrix4::scaling({-2.0f, 1.0f, 1.0f}); + * @endcode + * @see yScale(), zScale(), xAxis() + */ + inline constexpr static Vector3 xScale(T scale) { return Vector3(scale, T(1), T(1)); } + + /** + * @brief Scaling vector in direction of Y axis + * + * See xScale() for more information. + * @see yAxis() + */ + inline constexpr static Vector3 yScale(T scale) { return Vector3(T(1), scale, T(1)); } + + /** + * @brief Scaling vector in direction of Z axis + * + * See xScale() for more information. + * @see zAxis() + */ + inline constexpr static Vector3 zScale(T scale) { return Vector3(T(1), T(1), scale); } + /** * @brief Cross product * @@ -92,11 +142,18 @@ template class Vector3: public Vector<3, T> { MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION(Vector3, 3) }; -/** @debugoperator{Vector3} */ +MAGNUM_VECTOR_SUBCLASS_OPERATOR_IMPLEMENTATION(Vector3, 3) + +/** @debugoperator{Magnum::Math::Vector3} */ template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Math::Vector3& value) { return debug << static_cast&>(value); } }} +namespace Corrade { namespace Utility { + /** @configurationvalue{Magnum::Math::Vector3} */ + template struct ConfigurationValue>: public ConfigurationValue> {}; +}} + #endif diff --git a/src/Math/Vector4.h b/src/Math/Vector4.h index 32d4f3c51..303361921 100644 --- a/src/Math/Vector4.h +++ b/src/Math/Vector4.h @@ -23,7 +23,11 @@ namespace Magnum { namespace Math { -/** @brief Four-component vector */ +/** +@brief Four-component vector + +@configurationvalueref{Magnum::Math::Vector4} +*/ template class Vector4: public Vector<4, T> { public: /** @@ -86,11 +90,18 @@ template class Vector4: public Vector<4, T> { MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION(Vector4, 4) }; -/** @debugoperator{Vector4} */ +MAGNUM_VECTOR_SUBCLASS_OPERATOR_IMPLEMENTATION(Vector4, 4) + +/** @debugoperator{Magnum::Math::Vector4} */ template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug debug, const Magnum::Math::Vector4& value) { return debug << static_cast&>(value); } }} +namespace Corrade { namespace Utility { + /** @configurationvalue{Magnum::Math::Vector4} */ + template struct ConfigurationValue>: public ConfigurationValue> {}; +}} + #endif diff --git a/src/Mesh.cpp b/src/Mesh.cpp index 59d61c01d..85dd74b8e 100644 --- a/src/Mesh.cpp +++ b/src/Mesh.cpp @@ -20,7 +20,18 @@ using namespace std; namespace Magnum { -Mesh::~Mesh() { +Mesh::Mesh(Mesh&& other): + #ifndef MAGNUM_TARGET_GLES + vao(other.vao), + #endif + _primitive(other._primitive), _vertexCount(other._vertexCount), finalized(other.finalized), _buffers(other._buffers), _attributes(other._attributes) +{ + #ifndef MAGNUM_TARGET_GLES + other.vao = 0; + #endif +} + +void Mesh::destroy() { for(auto it = _buffers.begin(); it != _buffers.end(); ++it) delete it->first; @@ -29,6 +40,25 @@ Mesh::~Mesh() { #endif } +Mesh& Mesh::operator=(Mesh&& other) { + destroy(); + + #ifndef MAGNUM_TARGET_GLES + vao = other.vao; + #endif + _primitive = other._primitive; + _vertexCount = other._vertexCount; + finalized = other.finalized; + _buffers = other._buffers; + _attributes = other._attributes; + + #ifndef MAGNUM_TARGET_GLES + other.vao = 0; + #endif + + return *this; +} + Buffer* Mesh::addBuffer(BufferType interleaved) { Buffer* buffer = new Buffer(Buffer::Target::Array); _buffers.insert(make_pair(buffer, make_pair(interleaved, vector()))); diff --git a/src/Mesh.h b/src/Mesh.h index 7985bdd68..e75567d8b 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -30,7 +30,7 @@ namespace Magnum { class Buffer; -/** @ingroup rendering mesh +/** @brief Base class for managing non-indexed meshes VAOs are used for desktop OpenGL (not in OpenGL ES). @@ -48,9 +48,7 @@ VAOs are used for desktop OpenGL (not in OpenGL ES). */ class MAGNUM_EXPORT Mesh { Mesh(const Mesh& other) = delete; - Mesh(Mesh&& other) = delete; Mesh& operator=(const Mesh& other) = delete; - Mesh& operator=(Mesh&& other) = delete; public: #ifndef MAGNUM_TARGET_GLES @@ -208,12 +206,18 @@ class MAGNUM_EXPORT Mesh { #endif } + /** @brief Move constructor */ + Mesh(Mesh&& other); + /** * @brief Destructor * * Deletes all associated buffers. */ - virtual ~Mesh(); + inline virtual ~Mesh() { destroy(); } + + /** @brief Move assignment */ + Mesh& operator=(Mesh&& other); /** * @brief Whether the mesh is finalized @@ -340,6 +344,8 @@ class MAGNUM_EXPORT Mesh { std::set _attributes; MAGNUM_EXPORT void bindAttribute(Buffer* buffer, GLuint attribute, GLint size, Type type); + + void destroy(); }; } diff --git a/src/MeshTools/Clean.h b/src/MeshTools/Clean.h index e75918af2..c2e45cddd 100644 --- a/src/MeshTools/Clean.h +++ b/src/MeshTools/Clean.h @@ -121,7 +121,7 @@ template class Clean { } #endif -/** @ingroup mesh +/** @brief %Clean the mesh @tparam Vertex Vertex data type @tparam vertexSize How many initial vertex fields are important (for diff --git a/src/MeshTools/CombineIndexedArrays.h b/src/MeshTools/CombineIndexedArrays.h index d3b272e8d..612e8fce3 100644 --- a/src/MeshTools/CombineIndexedArrays.h +++ b/src/MeshTools/CombineIndexedArrays.h @@ -89,7 +89,7 @@ class CombineIndexedArrays { } #endif -/** @ingroup mesh +/** @brief Combine indexed arrays @param[in,out] indexedArrays Index and attribute arrays @return Array with resulting indices diff --git a/src/MeshTools/CompressIndices.h b/src/MeshTools/CompressIndices.h index 29a64d62d..d67eb9a4f 100644 --- a/src/MeshTools/CompressIndices.h +++ b/src/MeshTools/CompressIndices.h @@ -78,10 +78,6 @@ class CompressIndices { } #endif -/** @addtogroup mesh - * @{ - */ - /** @brief Compress vertex indices @param indices Index array @@ -124,8 +120,6 @@ inline void compressIndices(IndexedMesh* mesh, Buffer::Usage usage, const std::v return Implementation::CompressIndices{indices}(mesh, usage); } -/*@}*/ - }} #endif diff --git a/src/MeshTools/FlipNormals.h b/src/MeshTools/FlipNormals.h index 2c5be2216..191177945 100644 --- a/src/MeshTools/FlipNormals.h +++ b/src/MeshTools/FlipNormals.h @@ -24,10 +24,6 @@ namespace Magnum { namespace MeshTools { -/** @addtogroup mesh - * @{ - */ - /** @brief Flip face winding @@ -61,8 +57,6 @@ inline void flipNormals(std::vector& indices, std::vector flipNormals(normals); } -/*@}*/ - }} #endif diff --git a/src/MeshTools/GenerateFlatNormals.h b/src/MeshTools/GenerateFlatNormals.h index 1af39d95e..a5dfcbb9b 100644 --- a/src/MeshTools/GenerateFlatNormals.h +++ b/src/MeshTools/GenerateFlatNormals.h @@ -26,7 +26,7 @@ namespace Magnum { namespace MeshTools { -/** @ingroup mesh +/** @brief Generate flat normals @param indices Array of triangle face indexes @param vertices Vertex array diff --git a/src/MeshTools/Interleave.h b/src/MeshTools/Interleave.h index aa1de8558..3c7408e52 100644 --- a/src/MeshTools/Interleave.h +++ b/src/MeshTools/Interleave.h @@ -96,10 +96,6 @@ class Interleave { } #endif -/** @addtogroup mesh - * @{ - */ - /** @brief %Interleave vertex attributes @param attribute First attribute array @@ -157,8 +153,6 @@ template inline void interleave(Mesh* mesh, Buffer* buffer, Buffer:: return Implementation::Interleave()(mesh, buffer, usage, attributes...); } -/*@}*/ - }} #endif diff --git a/src/MeshTools/Subdivide.h b/src/MeshTools/Subdivide.h index 7dad9a0b7..ab2c27dc8 100644 --- a/src/MeshTools/Subdivide.h +++ b/src/MeshTools/Subdivide.h @@ -84,7 +84,7 @@ template class Subdivide { } #endif -/** @ingroup mesh +/** @brief %Subdivide the mesh @tparam Vertex Vertex data type @tparam Interpolator See `interpolator` function parameter diff --git a/src/MeshTools/Tipsify.h b/src/MeshTools/Tipsify.h index 58e2425e8..d43f4e926 100644 --- a/src/MeshTools/Tipsify.h +++ b/src/MeshTools/Tipsify.h @@ -51,7 +51,7 @@ class MESHTOOLS_EXPORT Tipsify { } #endif -/** @ingroup mesh +/** @brief %Tipsify the mesh @param[in,out] indices Indices array to operate on @param[in] vertexCount Vertex count diff --git a/src/Object.cpp b/src/Object.cpp deleted file mode 100644 index 66614f7ed..000000000 --- a/src/Object.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - Copyright © 2010, 2011, 2012 Vladimír Vondruš - - This file is part of Magnum. - - Magnum is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 3 - only, as published by the Free Software Foundation. - - Magnum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License version 3 for more details. -*/ - -#include "Object.h" - -#include -#include "Scene.h" -#include "Camera.h" - -using namespace std; - -namespace Magnum { - -Object* Object::setParent(Object* parent) { - /* Skip if nothing to do or this is scene */ - if(_parent == parent || _parent == this) return this; - - /* Add the object to children list of new parent */ - if(parent != nullptr) { - - /* Only Fry can be his own grandfather */ - Object* p = parent; - while(p != nullptr && p->parent() != p) { - if(p == this) return this; - p = p->parent(); - } - - parent->_children.insert(this); - } - - /* Remove the object from old parent children list */ - if(_parent != nullptr) - _parent->_children.erase(this); - - /* Set new parent */ - _parent = parent; - - setDirty(); - return this; -} - -Matrix4 Object::absoluteTransformation(Camera* camera) { - /* Shortcut for absolute transformation of camera relative to itself */ - if(camera == this) return Matrix4(); - - Matrix4 t = _transformation; - - Object* p = parent(); - while(p != nullptr) { - t = p->transformation()*t; - - /* We got to the scene, multiply with camera matrix */ - if(p->parent() == p) { - if(camera) { - CORRADE_ASSERT(camera->scene() == scene(), "Object::absoluteTransformation(): the camera is not part of the same scene as object!", t); - t = camera->cameraMatrix()*t; - } - - break; - } - - p = p->parent(); - } - - CORRADE_ASSERT(p != nullptr || camera == nullptr, "Object::absoluteTransformation(): the object is not part of camera scene!", t); - - return t; -} - -Object::~Object() { - /* Remove the object from parent's children */ - setParent(nullptr); - - /* Delete all children */ - while(!_children.empty()) - delete *_children.begin(); -} - -Scene* Object::scene() { - /* Goes up the family tree until it finds object which is parent of itself - (that's the scene) */ - Object* p = parent(); - while(p != nullptr) { - if(p->parent() == p) return static_cast(p); - p = p->parent(); - } - - return nullptr; -} - -Object* Object::setTransformation(const Matrix4& transformation) { - if(_parent == this) return this; - - _transformation = transformation; - setDirty(); - return this; -} - -void Object::setDirty() { - /* The object (and all its children) are already dirty, nothing to do */ - if(dirty) return; - - dirty = true; - - /* Make all children dirty */ - for(set::iterator it = _children.begin(); it != _children.end(); ++it) - (*it)->setDirty(); -} - -void Object::setClean() { - /* The object (and all its parents) are already clean, nothing to do */ - if(!dirty) return; - - /* Collect all parents */ - stack objects; - Object* p = this; - for(;;) { - objects.push(p); - - /* Stop on root object / scene / clean object */ - if(p->parent() == nullptr || p->parent() == p || !p->parent()->isDirty()) - break; - - p = p->parent(); - } - - /* Call setClean(const Matrix4&) for every parent and also this object */ - Object* o = objects.top(); - objects.pop(); - Matrix4 absoluteTransformation = o->absoluteTransformation(); - o->clean(absoluteTransformation); - while(!objects.empty()) { - o = objects.top(); - objects.pop(); - absoluteTransformation = absoluteTransformation*o->transformation(); - o->clean(absoluteTransformation); - } -} - -} diff --git a/src/Object.h b/src/Object.h deleted file mode 100644 index d4df67530..000000000 --- a/src/Object.h +++ /dev/null @@ -1,274 +0,0 @@ -#ifndef Magnum_Object_h -#define Magnum_Object_h -/* - Copyright © 2010, 2011, 2012 Vladimír Vondruš - - This file is part of Magnum. - - Magnum is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 3 - only, as published by the Free Software Foundation. - - Magnum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License version 3 for more details. -*/ - -/** @file - * @brief Class Magnum::Object - */ - -#include - -#include "Magnum.h" - -namespace Magnum { - -class Scene; -class Camera; - -/** -@todo User-specified Object implementation: -- for front-to-back sorting, LoD changes etc. -- for different parent/children implementation (e.g. no std::set, direct - access to scene etc.) -- for using doubles/halves instead of floats -- for using quat + position instead of matrices (where (asymmetric) scaling is - not needed) -*/ - -/** @ingroup scene - * @brief Base for all positioned objects - * - * @todo Transform transformation when changing parent, so the object stays in - * place. - */ -class MAGNUM_EXPORT Object { - Object(const Object& other) = delete; - Object(Object&& other) = delete; - Object& operator=(const Object& other) = delete; - Object& operator=(Object&& other) = delete; - - friend class Scene; - - public: - /** - * @brief Constructor - * @param parent Parent object - * - * Sets all transformations to their default values. - */ - inline Object(Object* parent = nullptr): _parent(nullptr), dirty(true) { - setParent(parent); - } - - /** - * @brief Destructor - * - * Removes itself from parent's children list and destroys all own - * children. - */ - virtual ~Object(); - - /** @{ @name Scene hierarchy */ - - /** - * @brief %Scene - * @return If the object is not assigned to any scene, returns nullptr. - */ - Scene* scene(); - - /** @brief Parent object */ - inline Object* parent() { return _parent; } - - /** @brief Child objects */ - inline const std::set& children() { return _children; } - - /** @brief Set parent object */ - Object* setParent(Object* parent); - - /*@}*/ - - /** @{ @name Object transformation - * - * All transformations (except absoluteTransformation()) are relative - * to parent. - */ - - /** @brief Transformation type */ - enum class Transformation: char { - /** Global transformation, applied after all other transformations. */ - Global = 0x00, - - /** Local transformation, applied before all other transformations. */ - Local = 0x01 - }; - - /** @brief Transformation */ - inline Matrix4 transformation() const { - return _transformation; - } - - /** - * @brief Absolute transformation - * - * Returns absolute transformation matrix relative to the camera or - * root object, if no camera is specified. If the camera is specified, - * it should be part of the same scene as object. - * - * Note that the absolute transformation is computed from all parent - * objects every time it is asked, unless this function is - * reimplemented in a different way. - */ - virtual Matrix4 absoluteTransformation(Camera* camera = nullptr); - - /** @brief Set transformation */ - Object* setTransformation(const Matrix4& transformation); - - /** - * @brief Multiply transformation - * @param transformation Transformation - * @param type Transformation type - */ - inline Object* multiplyTransformation(const Matrix4& transformation, Transformation type = Transformation::Global) { - setTransformation(type == Transformation::Global ? - transformation*_transformation : _transformation*transformation); - return this; - } - - /** - * @brief Translate object - * - * Same as calling multiplyTransformation() with Matrix4::translation(). - */ - inline Object* translate(Vector3 vec, Transformation type = Transformation::Global) { - multiplyTransformation(Matrix4::translation(vec), type); - return this; - } - - /** - * @brief Scale object - * - * Same as calling multiplyTransformation() with Matrix4::scaling(). - */ - inline Object* scale(Vector3 vec, Transformation type = Transformation::Global) { - multiplyTransformation(Matrix4::scaling(vec), type); - return this; - } - - /** - * @brief Rotate object - * - * Same as calling multiplyTransformation() with Matrix4::rotation(). - */ - inline Object* rotate(GLfloat angle, Vector3 vec, Transformation type = Transformation::Global) { - multiplyTransformation(Matrix4::rotation(angle, vec), type); - return this; - } - - /*@}*/ - - /** - * @brief Draw object - * @param transformationMatrix %Matrix specifying object - * transformation relative to the scene. - * @param camera Active camera (containing - * projection matrix) - * - * Default implementation does nothing. - */ - virtual void draw(const Matrix4& transformationMatrix, Camera* camera); - - /** @{ @name Caching helpers - * - * If the object (absolute) transformation or anything depending on it - * is used many times when drawing (such as e.g. position of light - * object), it's good to cache these values, so they don't have to be - * recalculated again on every request. - * - * If setDirty() is called on an object (or the object is transformed), - * it and all its children are marked as dirty. If any object is - * already dirty, it and all its children are skipped, because they - * are already dirty too. - * - * If setClean() is called on an object, it and all its parents are - * cleaned. If any object is already clean, it and all its parents are - * skipped, because they are already clean too. - * - * These functions are used to manage dirty status of the object. If - * the object doesn't cache anything, it's no need to bother about - * them, but if does, clean() should be reimplemented and used to - * regenerate the cache. - */ - - /** - * @brief Whether the object is dirty - * @return True, if transformation of the object, any parent or camera - * has changed since last asking, false otherwise. - */ - inline bool isDirty() const { return dirty; } - - /** - * @brief Set object and all its children as dirty - * - * Recursively calls setDirty() on every child. If the object is - * already marked as dirty, the function does nothing. It is usually - * not needed to reimplement this function, only if you for example - * need to reset some state on object which is not child of this. All - * computations should be done in setClean(). - * - * Reimplementations should call this function at the end, i.e.: - * @code - * void setDirty() { - * // ... - * - * Object::setDirty(); - * } - * @endcode - */ - virtual void setDirty(); - - /** - * @brief Set object and all its parents as clean - * - * Recursively calls clean() on every parent which is not already - * clean. - */ - void setClean(); - - protected: - /** - * @brief Clean the object - * - * When reimplementing, use absolute transformation passed as - * parameter instead of absoluteTransformation(), which is not - * efficient. The reimplementation should call this function at the - * beginning, i.e.: - * @code - * void clean(const Matrix4& absoluteTransformation) { - * Object::clean(absoluteTransformation); - * - * // ... - * } - * @endcode - */ - virtual void clean(const Matrix4& absoluteTransformation); - - /*@}*/ - - private: - Object* _parent; - std::set _children; - Matrix4 _transformation; - bool dirty; -}; - -/* Implementations for inline functions with unused parameters */ -inline void Object::draw(const Matrix4&, Camera*) {} -inline void Object::clean(const Matrix4&) { dirty = false; } - - -} - -#endif diff --git a/src/Physics/ShapeGroup.h b/src/Physics/ShapeGroup.h index 2e1174c82..074474719 100644 --- a/src/Physics/ShapeGroup.h +++ b/src/Physics/ShapeGroup.h @@ -31,7 +31,7 @@ namespace Magnum { namespace Physics { /** @brief Collider group with defined set operation -Result of union, intersection, substraction or XOR of two collider objects. +Result of union, intersection, subtraction or XOR of two collider objects. See @ref collision-detection for brief introduction. */ class PHYSICS_EXPORT ShapeGroup: public AbstractShape { diff --git a/src/Primitives/Capsule.h b/src/Primitives/Capsule.h index 79c715231..3923beba1 100644 --- a/src/Primitives/Capsule.h +++ b/src/Primitives/Capsule.h @@ -23,7 +23,7 @@ namespace Magnum { namespace Primitives { -/** @ingroup mesh +/** @brief %Capsule primitive Cylinder along Y axis with hemispheres instead of caps. diff --git a/src/Primitives/Cube.h b/src/Primitives/Cube.h index 0a7864b91..bacc1829c 100644 --- a/src/Primitives/Cube.h +++ b/src/Primitives/Cube.h @@ -23,9 +23,7 @@ namespace Magnum { namespace Primitives { -/** @ingroup mesh -@brief %Cube primitive -*/ +/** @brief %Cube primitive */ class Cube: public Trade::MeshData { public: /** @brief Constructor */ diff --git a/src/Primitives/Icosphere.h b/src/Primitives/Icosphere.h index e81bf8c78..aaf284cc3 100644 --- a/src/Primitives/Icosphere.h +++ b/src/Primitives/Icosphere.h @@ -27,7 +27,7 @@ namespace Magnum { namespace Primitives { template class Icosphere; -/** @ingroup mesh +/** @brief %Icosphere primitive with zero subdivisions @todo Use own computed (and more precise) icosahedron data, not these stolen @@ -39,7 +39,7 @@ template<> class Icosphere<0>: public Trade::MeshData { Icosphere(); }; -/** @ingroup mesh +/** * @brief %Icosphere primitive * @tparam subdivisions Number of subdivisions */ diff --git a/src/Primitives/Plane.h b/src/Primitives/Plane.h index dffe9ce4e..d21eee942 100644 --- a/src/Primitives/Plane.h +++ b/src/Primitives/Plane.h @@ -23,7 +23,7 @@ namespace Magnum { namespace Primitives { -/** @ingroup mesh +/** @brief %Plane primitive 2x2 plane with normals in positive Z direction. diff --git a/src/Primitives/UVSphere.h b/src/Primitives/UVSphere.h index 6f50caee4..4e6be3b63 100644 --- a/src/Primitives/UVSphere.h +++ b/src/Primitives/UVSphere.h @@ -23,9 +23,7 @@ namespace Magnum { namespace Primitives { -/** @ingroup mesh -@brief UV Sphere primitive -*/ +/** @brief UV Sphere primitive */ class UVSphere: public Capsule { public: /** diff --git a/src/Query.h b/src/Query.h index 38d4d1cf1..32413b2bf 100644 --- a/src/Query.h +++ b/src/Query.h @@ -24,10 +24,6 @@ namespace Magnum { #ifndef MAGNUM_TARGET_GLES -/** @addtogroup rendering - * @{ - */ - /** @brief Base class for queries @@ -301,7 +297,6 @@ class TimeQuery: public AbstractQuery { } }; -/*@}*/ #endif } diff --git a/src/Renderbuffer.h b/src/Renderbuffer.h index 3a9d14145..35854aba3 100644 --- a/src/Renderbuffer.h +++ b/src/Renderbuffer.h @@ -23,7 +23,7 @@ namespace Magnum { -/** @ingroup textures +/** @brief %Renderbuffer Attachable to Framebuffer as render target. diff --git a/src/Scene.h b/src/Scene.h deleted file mode 100644 index 076e6f4ef..000000000 --- a/src/Scene.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef Magnum_Scene_h -#define Magnum_Scene_h -/* - Copyright © 2010, 2011, 2012 Vladimír Vondruš - - This file is part of Magnum. - - Magnum is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 3 - only, as published by the Free Software Foundation. - - Magnum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License version 3 for more details. -*/ - -/** @file - * @brief Class Magnum::Scene - */ - -#include "Object.h" - -namespace Magnum { - -/** @ingroup scene -@brief %Scene -*/ -class MAGNUM_EXPORT Scene: public Object { - public: - /** @brief Constructor */ - inline Scene() { _parent = this; } - - void setParent(Object* parent) = delete; - void setTransformation(const Matrix4& transformation) = delete; - void multiplyTransformation(const Matrix4& transformation, Transformation type = Transformation::Global) = delete; - void translate(Vector3 vec, Transformation type = Transformation::Global) = delete; - void scale(Vector3 vec, Transformation type = Transformation::Global) = delete; - void rotate(GLfloat angle, Vector3 vec, Transformation type = Transformation::Global) = delete; - - private: - inline void draw(const Magnum::Matrix4&, Camera*) {} -}; - -} - -#endif diff --git a/src/SceneGraph/CMakeLists.txt b/src/SceneGraph/CMakeLists.txt new file mode 100644 index 000000000..dd8f5bee3 --- /dev/null +++ b/src/SceneGraph/CMakeLists.txt @@ -0,0 +1,42 @@ +# Files shared between main library and unit test library +set(MagnumSceneGraph_SRCS + Camera.cpp + Light.cpp) +set(MagnumSceneGraph_HEADERS + Camera.h + Light.h + Object.h + Scene.h + + magnumSceneGraphVisibility.h) +add_library(MagnumSceneGraphObjects OBJECT ${MagnumSceneGraph_SRCS}) + +# Files compiled with different flags for main library and unit test library +set(MagnumSceneGraph_GracefulAssert_SRCS + Object.cpp) + +# Set shared library flags for the objects, as they will be part of shared lib +# TODO: fix when CMake sets target_EXPORTS for OBJECT targets as well +set_target_properties(MagnumSceneGraphObjects PROPERTIES COMPILE_FLAGS "-DMagnumSceneGraphObjects_EXPORTS ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") + +# SceneGraph library +add_library(MagnumSceneGraph SHARED + $ + ${MagnumSceneGraph_GracefulAssert_SRCS}) +target_link_libraries(MagnumSceneGraph Magnum) + +install(TARGETS MagnumSceneGraph DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) +install(FILES ${MagnumSceneGraph_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/SceneGraph) + +if(BUILD_TESTS) + enable_testing() + + # Library with graceful assert for testing + add_library(MagnumSceneGraphTestLib SHARED + $ + ${MagnumSceneGraph_GracefulAssert_SRCS}) + set_target_properties(MagnumSceneGraphTestLib PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) + target_link_libraries(MagnumSceneGraphTestLib Magnum) + + add_subdirectory(Test) +endif() diff --git a/src/SceneGraph/Camera.cpp b/src/SceneGraph/Camera.cpp new file mode 100644 index 000000000..f6896667b --- /dev/null +++ b/src/SceneGraph/Camera.cpp @@ -0,0 +1,130 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "Camera.h" +#include "Framebuffer.h" +#include "Scene.h" + +using namespace std; + +namespace Magnum { namespace SceneGraph { + +#ifndef DOXYGEN_GENERATING_OUTPUT +namespace Implementation { + +template MatrixType aspectRatioFix(AspectRatioPolicy aspectRatioPolicy, const Vector2& projectionScale, const Math::Vector2& viewport) { + /* Don't divide by zero / don't preserve anything */ + if(projectionScale.x() == 0 || projectionScale.y() == 0 || viewport.x() == 0 || viewport.y() == 0 || aspectRatioPolicy == AspectRatioPolicy::NotPreserved) + return MatrixType(); + + Vector2 relativeAspectRatio = Vector2::from(viewport)*projectionScale; + + /* Extend on larger side = scale larger side down + Clip on smaller side = scale smaller side up */ + return Camera::aspectRatioScale( + (relativeAspectRatio.x() > relativeAspectRatio.y()) == (aspectRatioPolicy == AspectRatioPolicy::Extend) ? + Vector2(relativeAspectRatio.y()/relativeAspectRatio.x(), 1.0f) : + Vector2(1.0f, relativeAspectRatio.x()/relativeAspectRatio.y())); +} + +/* Explicitly instantiate the templates */ +template Matrix3 aspectRatioFix(AspectRatioPolicy, const Vector2&, const Math::Vector2&); +template Matrix4 aspectRatioFix(AspectRatioPolicy, const Vector2&, const Math::Vector2&); + +} +#endif + +template Camera::Camera(ObjectType* parent): ObjectType(parent), _aspectRatioPolicy(AspectRatioPolicy::NotPreserved) {} + +template void Camera::setViewport(const Math::Vector2& size) { + Framebuffer::setViewport({0, 0}, size); + + _viewport = size; + fixAspectRatio(); +} + +template void Camera::clean(const MatrixType& absoluteTransformation) { + ObjectType::clean(absoluteTransformation); + + _cameraMatrix = absoluteTransformation.inverted(); +} + +template void Camera::draw() { + SceneType* s = this->scene(); + CORRADE_ASSERT(s, "Camera: cannot draw without camera attached to scene", ); + + Framebuffer::clear(); + + /* Recursively draw child objects */ + drawChildren(s, cameraMatrix()); +} + +template void Camera::drawChildren(ObjectType* object, const MatrixType& transformationMatrix) { + for(ObjectType* i = object->firstChild(); i; i = i->nextSibling()) { + /* Transformation matrix for the object */ + MatrixType matrix = transformationMatrix*i->transformation(); + + /* Draw the object and its children */ + i->draw(matrix, static_cast(this)); + drawChildren(i, matrix); + } +} + +void Camera2D::setProjection(const Vector2& size) { + /* Scale the volume down so it fits in (-1, 1) in all directions */ + rawProjectionMatrix = Matrix3::scaling(2.0f/size); + + fixAspectRatio(); +} + +void Camera3D::setOrthographic(const Vector2& size, GLfloat near, GLfloat far) { + _near = near; + _far = far; + + Vector2 xyScale = 2.0f/size; + GLfloat zScale = 2.0f/(near-far); + + rawProjectionMatrix = Matrix4( + xyScale.x(), 0.0f, 0.0f, 0.0f, + 0.0f, xyScale.y(), 0.0f, 0.0f, + 0.0f, 0.0f, zScale, 0.0f, + 0.0f, 0.0f, near*zScale-1, 1.0f + ); + + fixAspectRatio(); +} + +void Camera3D::setPerspective(GLfloat fov, GLfloat near, GLfloat far) { + _near = near; + _far = far; + + GLfloat xyScale = 1.0f/tan(fov/2); /* == near/size */ + GLfloat zScale = 1.0f/(near-far); + + rawProjectionMatrix = Matrix4( + xyScale, 0.0f, 0.0f, 0.0f, + 0.0f, xyScale, 0.0f, 0.0f, + 0.0f, 0.0f, (far+near)*zScale, -1.0f, + 0.0f, 0.0f, (2*far*near)*zScale, 0.0f + ); + + fixAspectRatio(); +} + +/* Explicitly instantiate the templates */ +template class Camera; +template class Camera; + +}} diff --git a/src/SceneGraph/Camera.h b/src/SceneGraph/Camera.h new file mode 100644 index 000000000..76faa6c45 --- /dev/null +++ b/src/SceneGraph/Camera.h @@ -0,0 +1,247 @@ +#ifndef Magnum_SceneGraph_Camera_h +#define Magnum_SceneGraph_Camera_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::SceneGraph::Camera, Magnum::SceneGraph::Camera2D, Magnum::SceneGraph::Camera3D + */ + +#include "Object.h" + +#ifdef WIN32 /* I so HATE windows.h */ +#undef near +#undef far +#endif + +namespace Magnum { namespace SceneGraph { + +/** @todo Export implementation symbols only for tests */ + +#ifndef DOXYGEN_GENERATING_OUTPUT +namespace Implementation { + enum class AspectRatioPolicy { + NotPreserved, Extend, Clip + }; + + template class Camera {}; + + template MatrixType aspectRatioFix(AspectRatioPolicy aspectRatioPolicy, const Vector2& projectionScale, const Math::Vector2& viewport); + + /* These templates are instantiated in source file */ + extern template SCENEGRAPH_EXPORT Matrix3 aspectRatioFix(AspectRatioPolicy, const Vector2&, const Math::Vector2&); + extern template SCENEGRAPH_EXPORT Matrix4 aspectRatioFix(AspectRatioPolicy, const Vector2&, const Math::Vector2&); +} +#endif + +/** +@brief %Camera object + */ +template class SCENEGRAPH_EXPORT Camera: public ObjectType { + public: + /** + * @brief Aspect ratio policy + * + * @see aspectRatioPolicy(), setAspectRatioPolicy() + */ + #ifndef DOXYGEN_GENERATING_OUTPUT + typedef Implementation::AspectRatioPolicy AspectRatioPolicy; + #else + enum class AspectRatioPolicy { + NotPreserved, /**< Don't preserve aspect ratio (default) */ + Extend, /**< Extend on larger side of view */ + Clip /**< Clip on smaller side of view */ + }; + #endif + + /** @copydoc Object::Object */ + Camera(ObjectType* parent = nullptr); + + /** @brief Aspect ratio policy */ + inline AspectRatioPolicy aspectRatioPolicy() const { return _aspectRatioPolicy; } + + /** @brief Set aspect ratio policy */ + void setAspectRatioPolicy(AspectRatioPolicy policy) { _aspectRatioPolicy = policy; } + + /** + * @brief Camera matrix + * + * Camera matrix describes world position relative to the camera and is + * applied as first. + */ + inline MatrixType cameraMatrix() { + this->setClean(); + return _cameraMatrix; + } + + /** + * @brief Projection matrix + * + * Projection matrix handles e.g. perspective distortion and is applied + * as last. + * @see projectionSize() + */ + inline MatrixType projectionMatrix() const { return _projectionMatrix; } + + /** + * @brief Size of (near) XY plane in current projection + * + * Returns size of near XY plane computed from projection matrix. + * @see projectionMatrix() + */ + inline Vector2 projectionSize() const { + return {2.0f/rawProjectionMatrix[0].x(), 2.0f/rawProjectionMatrix[1].y()}; + } + + /** @brief Viewport size */ + inline Math::Vector2 viewport() const { return _viewport; } + + /** + * @brief Set viewport size + * + * Call when window size changes. + * + * Calls Framebuffer::setViewport() and stores viewport size + * internally. + */ + virtual void setViewport(const Math::Vector2& size); + + /** + * @brief Draw the scene + * + * Calls Framebuffer::clear() and draws the scene using drawChildren(). + */ + virtual void draw(); + + using ObjectType::draw; /* Don't hide Object's draw() */ + + protected: + /** + * Recalculates camera matrix. + */ + void clean(const MatrixType& absoluteTransformation); + + /** + * @brief Draw object children + * + * Recursively draws all children of the object. + */ + void drawChildren(ObjectType* object, const MatrixType& transformationMatrix); + + #ifndef DOXYGEN_GENERATING_OUTPUT + inline void fixAspectRatio() { + _projectionMatrix = Implementation::aspectRatioFix(_aspectRatioPolicy, {rawProjectionMatrix[0].x(), rawProjectionMatrix[1].y()}, _viewport)*rawProjectionMatrix; + } + + MatrixType rawProjectionMatrix; + AspectRatioPolicy _aspectRatioPolicy; + #endif + + private: + MatrixType _projectionMatrix; + MatrixType _cameraMatrix; + + Math::Vector2 _viewport; +}; + +#ifndef DOXYGEN_GENERATING_OUTPUT +/* These templates are instantiated in source file */ +extern template class SCENEGRAPH_EXPORT Camera; +extern template class SCENEGRAPH_EXPORT Camera; + +namespace Implementation { + template<> class Camera<2> { + public: + inline constexpr static Matrix3 aspectRatioScale(const Vector2& scale) { + return Matrix3::scaling({scale.x(), scale.y()}); + } + }; + template<> class Camera<3> { + public: + inline constexpr static Matrix4 aspectRatioScale(const Vector2& scale) { + return Matrix4::scaling({scale.x(), scale.y(), 1.0f}); + } + }; +} +#endif + +/** @brief %Camera for two-dimensional scenes */ +class SCENEGRAPH_EXPORT Camera2D: public Camera { + public: + /** + * @brief Constructor + * @param parent Parent object + * + * Sets orthographic projection to the default OpenGL cube (range @f$ [-1; 1] @f$ in all directions). + * @see setOrthographic() + */ + inline Camera2D(Object2D* parent = nullptr): Camera(parent) {} + + /** + * @brief Set projection + * @param size Size of the view + * + * The area of given size will be scaled down to range @f$ [-1; 1] @f$ + * on all directions. + */ + void setProjection(const Vector2& size); +}; + +/** @brief %Camera for three-dimensional scenes */ +class SCENEGRAPH_EXPORT Camera3D: public Camera { + public: + /** + * @brief Constructor + * @param parent Parent object + * + * Sets orthographic projection to the default OpenGL cube (range @f$ [-1; 1] @f$ in all directions). + * @see setOrthographic(), setPerspective() + */ + inline Camera3D(Object3D* parent = nullptr): Camera(parent), _near(0.0f), _far(0.0f) {} + + /** + * @brief Set orthographic projection + * @param size Size of the view + * @param near Near clipping plane + * @param far Far clipping plane + * + * The volume of given size will be scaled down to range @f$ [-1; 1] @f$ + * on all directions. + */ + void setOrthographic(const Vector2& size, GLfloat near, GLfloat far); + + /** + * @brief Set perspective projection + * @param fov Field of view angle + * @param near Near clipping plane + * @param far Far clipping plane + * + * @todo Aspect ratio + */ + void setPerspective(GLfloat fov, GLfloat near, GLfloat far); + + /** @brief Near clipping plane */ + inline GLfloat near() const { return _near; } + + /** @brief Far clipping plane */ + inline GLfloat far() const { return _far; } + + private: + GLfloat _near, _far; +}; + +}} + +#endif diff --git a/src/Light.cpp b/src/SceneGraph/Light.cpp similarity index 94% rename from src/Light.cpp rename to src/SceneGraph/Light.cpp index 9c12bfff2..9b5eb5860 100644 --- a/src/Light.cpp +++ b/src/SceneGraph/Light.cpp @@ -15,7 +15,7 @@ #include "Light.h" -namespace Magnum { +namespace Magnum { namespace SceneGraph { void Light::clean(const Matrix4& absoluteTransformation) { Object::clean(absoluteTransformation); @@ -23,4 +23,4 @@ void Light::clean(const Matrix4& absoluteTransformation) { _position = absoluteTransformation[3]; } -} +}} diff --git a/src/Light.h b/src/SceneGraph/Light.h similarity index 80% rename from src/Light.h rename to src/SceneGraph/Light.h index 1675753f4..3e604f584 100644 --- a/src/Light.h +++ b/src/SceneGraph/Light.h @@ -1,5 +1,5 @@ -#ifndef Magnum_Light_h -#define Magnum_Light_h +#ifndef Magnum_SceneGraph_Light_h +#define Magnum_SceneGraph_Light_h /* Copyright © 2010, 2011, 2012 Vladimír Vondruš @@ -16,25 +16,25 @@ */ /** @file - * @brief Class Magnum::Light + * @brief Class Magnum::SceneGraph::Light */ #include "Object.h" -namespace Magnum { +namespace Magnum { namespace SceneGraph { -/** @ingroup scene +/** * @brief Basic light object * * Provides cached light position. */ -class MAGNUM_EXPORT Light: public Object { +class SCENEGRAPH_EXPORT Light: public Object3D { public: /** * @brief Constructor * @param parent Parent object */ - inline Light(Object* parent = nullptr): Object(parent) {} + inline Light(Object3D* parent = nullptr): Object3D(parent) {} /** * @brief Light position relative to root object (scene) @@ -54,6 +54,6 @@ class MAGNUM_EXPORT Light: public Object { Vector4 _position; }; -} +}} #endif diff --git a/src/SceneGraph/Object.cpp b/src/SceneGraph/Object.cpp new file mode 100644 index 000000000..f3de86b1c --- /dev/null +++ b/src/SceneGraph/Object.cpp @@ -0,0 +1,147 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "Object.h" + +#include + +#include "Scene.h" +#include "Camera.h" + +using namespace std; +using namespace Magnum::Math; + +namespace Magnum { namespace SceneGraph { + +template ObjectType* Object::setParent(ObjectType* parent) { + /* Skip if nothing to do or this is scene */ + if(this->parent() == parent || isScene()) return static_cast(this); + + /* Only Fry can be his own grandfather */ + ObjectType* p = parent; + while(p) { + /** @todo Assert for this */ + if(p == this) return static_cast(this); + p = p->parent(); + } + + /* Remove the object from old parent children list */ + if(this->parent()) + this->parent()->cut(static_cast(this)); + + /* Add the object to list of new parent */ + if(parent) + parent->insert(static_cast(this)); + + setDirty(); + return static_cast(this); +} + +template MatrixType Object::absoluteTransformation(CameraType* camera) { + /* Shortcut for absolute transformation of camera relative to itself */ + if(camera == this) return MatrixType(); + + MatrixType t = _transformation; + + ObjectType* p = parent(); + while(p != nullptr) { + t = p->transformation()*t; + + /* We got to the scene, multiply with camera matrix */ + if(p->isScene()) { + if(camera) { + CORRADE_ASSERT(camera->scene() == scene(), "Object::absoluteTransformation(): the camera is not part of the same scene as object!", t); + t = camera->cameraMatrix()*t; + } + + break; + } + + p = p->parent(); + } + + CORRADE_ASSERT(p != nullptr || camera == nullptr, "Object::absoluteTransformation(): the object is not part of camera scene!", t); + + return t; +} + +template SceneType* Object::scene() { + /* Goes up the family tree until it finds object which is parent of itself + (that's the scene) */ + ObjectType* p = parent(); + while(p != nullptr) { + if(p->isScene()) return static_cast(p); + p = p->parent(); + } + + return nullptr; +} + +template ObjectType* Object::setTransformation(const MatrixType& transformation) { + /* Setting transformation is forbidden for the scene */ + /** @todo Assert for this? */ + if(isScene()) return static_cast(this); + + _transformation = transformation; + setDirty(); + return static_cast(this); +} + +template void Object::setDirty() { + /* The object (and all its children) are already dirty, nothing to do */ + if(dirty) return; + + dirty = true; + + /* Make all children dirty */ + for(ObjectType* i = firstChild(); i; i = i->nextSibling()) + i->setDirty(); +} + +template void Object::setClean() { + /* The object (and all its parents) are already clean, nothing to do */ + if(!dirty) return; + + /* Collect all parents */ + stack objects; + ObjectType* p = static_cast(this); + for(;;) { + objects.push(p); + + /* Stop on root object / clean object */ + if(p->parent() == nullptr || !p->parent()->isDirty()) + break; + + p = p->parent(); + } + + /* Call setClean(const Matrix4&) for every parent and also this object */ + ObjectType* o = objects.top(); + objects.pop(); + MatrixType absoluteTransformation = o->absoluteTransformation(); + o->clean(absoluteTransformation); + while(!objects.empty()) { + o = objects.top(); + objects.pop(); + absoluteTransformation = absoluteTransformation*o->transformation(); + o->clean(absoluteTransformation); + } +} + +/* Explicitly instantiate the templates */ +template class Object; +template class Object; + +}} diff --git a/src/SceneGraph/Object.h b/src/SceneGraph/Object.h new file mode 100644 index 000000000..b6a0d3861 --- /dev/null +++ b/src/SceneGraph/Object.h @@ -0,0 +1,366 @@ +#ifndef Magnum_SceneGraph_Object_h +#define Magnum_SceneGraph_Object_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::SceneGraph::Object, Magnum::SceneGraph::Object2D, Magnum::SceneGraph::Object3D + */ + +#include + +#include "Magnum.h" + +#include "magnumSceneGraphVisibility.h" + +namespace Magnum { namespace SceneGraph { + +/** +@todo User-specified Object implementation: +- for front-to-back sorting, LoD changes etc. +- for different parent/children implementation (e.g. no std::set, direct + access to scene etc.) +- for using doubles/halves instead of floats +- for using quat + position instead of matrices (where (asymmetric) scaling is + not needed) +*/ + +/** + * @brief Base for all positioned objects + * + * @todo Transform transformation when changing parent, so the object stays in + * place. + */ +template class SCENEGRAPH_EXPORT Object: public Corrade::Containers::LinkedList, public Corrade::Containers::LinkedListItem { + #ifndef DOXYGEN_GENERATING_OUTPUT + Object(const Object& other) = delete; + Object(Object&& other) = delete; + Object& operator=(const Object& other) = delete; + Object& operator=(Object&& other) = delete; + #endif + + public: + /** + * @brief Constructor + * @param parent Parent object + * + * Sets all transformations to their default values. + */ + inline Object(ObjectType* parent = nullptr): dirty(true) { + setParent(parent); + } + + /** + * @brief Destructor + * + * Removes itself from parent's children list and destroys all own + * children. + */ + virtual inline ~Object() {} + + /** @{ @name Scene hierarchy */ + + /** @brief Whether this object is scene */ + virtual inline bool isScene() const { return false; } + + /** + * @brief %Scene + * @return If the object is not assigned to any scene, returns nullptr. + */ + SceneType* scene(); + + /** @brief Parent object or `nullptr`, if this is root object */ + inline ObjectType* parent() { return Corrade::Containers::LinkedListItem::list(); } + + /** @brief Previous sibling object or `nullptr`, if this is first object */ + inline ObjectType* previousSibling() { return Corrade::Containers::LinkedListItem::previous(); } + + /** @brief Next sibling object or `nullptr`, if this is last object */ + inline ObjectType* nextSibling() { return Corrade::Containers::LinkedListItem::next(); } + + /** @brief Whether this object has children */ + inline bool hasChildren() const { return !Corrade::Containers::LinkedList::isEmpty(); } + + /** @brief First child object or `nullptr`, if this object has no children */ + inline ObjectType* firstChild() { return Corrade::Containers::LinkedList::first(); } + + /** @brief Last child object or `nullptr`, if this object has no children */ + inline ObjectType* lastChild() { return Corrade::Containers::LinkedList::last(); } + + /** @brief Set parent object */ + ObjectType* setParent(ObjectType* parent); + + /*@}*/ + + /** @{ @name Object transformation + * + * All transformations (except absoluteTransformation()) are relative + * to parent. + */ + + /** @brief Transformation type */ + enum class Transformation: char { + /** Global transformation, applied after all other transformations. */ + Global = 0x00, + + /** Local transformation, applied before all other transformations. */ + Local = 0x01 + }; + + /** @brief Transformation */ + inline MatrixType transformation() const { + return _transformation; + } + + /** + * @brief Absolute transformation + * + * Returns absolute transformation matrix relative to the camera or + * root object, if no camera is specified. If the camera is specified, + * it should be part of the same scene as object. + * + * Note that the absolute transformation is computed from all parent + * objects every time it is asked, unless this function is + * reimplemented in a different way. + */ + virtual MatrixType absoluteTransformation(CameraType* camera = nullptr); + + /** @brief Set transformation */ + ObjectType* setTransformation(const MatrixType& transformation); + + /** + * @brief Multiply transformation + * @param transformation Transformation + * @param type Transformation type + */ + inline ObjectType* multiplyTransformation(const MatrixType& transformation, Transformation type = Transformation::Global) { + setTransformation(type == Transformation::Global ? + transformation*_transformation : _transformation*transformation); + return static_cast(this); + } + + /*@}*/ + + /** + * @brief Draw object + * @param transformationMatrix %Matrix specifying object + * transformation relative to the scene. + * @param camera Active camera (containing + * projection matrix) + * + * Default implementation does nothing. + */ + virtual void draw(const MatrixType& transformationMatrix, CameraType* camera); + + /** @{ @name Caching helpers + * + * If the object (absolute) transformation or anything depending on it + * is used many times when drawing (such as e.g. position of light + * object), it's good to cache these values, so they don't have to be + * recalculated again on every request. + * + * If setDirty() is called on an object (or the object is transformed), + * it and all its children are marked as dirty. If any object is + * already dirty, it and all its children are skipped, because they + * are already dirty too. + * + * If setClean() is called on an object, it and all its parents are + * cleaned. If any object is already clean, it and all its parents are + * skipped, because they are already clean too. + * + * These functions are used to manage dirty status of the object. If + * the object doesn't cache anything, it's no need to bother about + * them, but if does, clean() should be reimplemented and used to + * regenerate the cache. + */ + + /** + * @brief Whether the object is dirty + * @return True, if transformation of the object, any parent or camera + * has changed since last asking, false otherwise. + */ + inline bool isDirty() const { return dirty; } + + /** + * @brief Set object and all its children as dirty + * + * Recursively calls setDirty() on every child. If the object is + * already marked as dirty, the function does nothing. It is usually + * not needed to reimplement this function, only if you for example + * need to reset some state on object which is not child of this. All + * computations should be done in setClean(). + * + * Reimplementations should call this function at the end, i.e.: + * @code + * void setDirty() { + * // ... + * + * Object::setDirty(); + * } + * @endcode + */ + virtual void setDirty(); + + /** + * @brief Set object and all its parents as clean + * + * Recursively calls clean() on every parent which is not already + * clean. + */ + void setClean(); + + protected: + /** + * @brief Clean the object + * + * When reimplementing, use absolute transformation passed as + * parameter instead of absoluteTransformation(), which is not + * efficient. The reimplementation should call this function at the + * beginning, i.e.: + * @code + * void clean(const Matrix4& absoluteTransformation) { + * Object::clean(absoluteTransformation); + * + * // ... + * } + * @endcode + */ + virtual void clean(const MatrixType& absoluteTransformation); + + /*@}*/ + + private: + /* Hide base class members, as they are aliased to more meaningful names */ + using Corrade::Containers::LinkedList::first; + using Corrade::Containers::LinkedList::last; + using Corrade::Containers::LinkedList::isEmpty; + using Corrade::Containers::LinkedList::insert; + using Corrade::Containers::LinkedList::cut; + using Corrade::Containers::LinkedList::move; + using Corrade::Containers::LinkedList::erase; + using Corrade::Containers::LinkedList::clear; + using Corrade::Containers::LinkedListItem::list; + using Corrade::Containers::LinkedListItem::previous; + using Corrade::Containers::LinkedListItem::next; + + MatrixType _transformation; + bool dirty; +}; + +/* Implementations for inline functions with unused parameters */ +template inline void Object::draw(const MatrixType&, CameraType*) {} +template inline void Object::clean(const MatrixType&) { dirty = false; } + +class Camera2D; +class Camera3D; +class Object2D; +class Object3D; +template class Scene; +typedef Scene Scene2D; +typedef Scene Scene3D; + +#ifndef DOXYGEN_GENERATING_OUTPUT +/* These templates are instantiated in source file */ +extern template class SCENEGRAPH_EXPORT Object; +extern template class SCENEGRAPH_EXPORT Object; +#endif + +/** @brief Two-dimensional object */ +class SCENEGRAPH_EXPORT Object2D: public Object { + public: + /** @copydoc Object::Object */ + inline Object2D(Object2D* parent = nullptr): Object(parent) {} + + /** + * @brief Translate object + * + * Same as calling multiplyTransformation() with Matrix3::translation(). + */ + inline Object2D* translate(const Vector2& vec, Transformation type = Transformation::Global) { + multiplyTransformation(Matrix3::translation(vec), type); + return this; + } + + /** + * @brief Scale object + * + * Same as calling multiplyTransformation() with Matrix3::scaling(). + */ + inline Object2D* scale(const Vector2& vec, Transformation type = Transformation::Global) { + multiplyTransformation(Matrix3::scaling(vec), type); + return this; + } + + /** + * @brief Rotate object + * + * Same as calling multiplyTransformation() with Matrix3::rotation(). + */ + inline Object2D* rotate(GLfloat angle, Transformation type = Transformation::Global) { + multiplyTransformation(Matrix3::rotation(angle), type); + return this; + } + + /** + * @brief Move object in stacking order + * @param under Sibling object under which to move or `nullptr`, + * if you want to move it above all. + */ + inline Object2D* move(Object2D* under) { + parent()->Corrade::Containers::LinkedList::move(this, under); + return this; + } +}; + +/** @brief Three-dimensional object */ +class SCENEGRAPH_EXPORT Object3D: public Object { + public: + /** @copydoc Object::Object */ + inline Object3D(Object3D* parent = nullptr): Object(parent) {} + + /** + * @brief Translate object + * + * Same as calling multiplyTransformation() with Matrix4::translation(). + */ + inline Object3D* translate(const Vector3& vec, Transformation type = Transformation::Global) { + multiplyTransformation(Matrix4::translation(vec), type); + return this; + } + + /** + * @brief Scale object + * + * Same as calling multiplyTransformation() with Matrix4::scaling(). + */ + inline Object3D* scale(const Vector3& vec, Transformation type = Transformation::Global) { + multiplyTransformation(Matrix4::scaling(vec), type); + return this; + } + + /** + * @brief Rotate object + * + * Same as calling multiplyTransformation() with Matrix4::rotation(). + */ + inline Object3D* rotate(GLfloat angle, const Vector3& vec, Transformation type = Transformation::Global) { + multiplyTransformation(Matrix4::rotation(angle, vec), type); + return this; + } +}; + +}} + +#endif diff --git a/src/SceneGraph/Scene.h b/src/SceneGraph/Scene.h new file mode 100644 index 000000000..2fd2e6fe9 --- /dev/null +++ b/src/SceneGraph/Scene.h @@ -0,0 +1,54 @@ +#ifndef Magnum_SceneGraph_Scene_h +#define Magnum_SceneGraph_Scene_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::SceneGraph::Scene, typedef Magnum::SceneGraph::Scene2D, Magnum::SceneGraph::Scene3D + */ + +#include "Object.h" + +namespace Magnum { namespace SceneGraph { + +/** @brief %Scene */ +template class SCENEGRAPH_EXPORT Scene: public ObjectType { + public: + /** @copydoc Object::isScene() */ + inline bool isScene() const { return true; } + + /** @todo Some deleted functions belong only to Scene2D, some only to Scene3D - what to do? */ + #ifndef DOXYGEN_GENERATING_OUTPUT + void setParent(ObjectType* parent) = delete; + void setTransformation(const MatrixType& transformation) = delete; + void multiplyTransformation(const MatrixType& transformation, typename ObjectType::Transformation type = ObjectType::Transformation::Global) = delete; + void translate(const VectorType& vec, typename ObjectType::Transformation type = ObjectType::Transformation::Global) = delete; + void scale(const VectorType& vec, typename ObjectType::Transformation type = ObjectType::Transformation::Global) = delete; + void rotate(GLfloat angle, const VectorType& vec, typename ObjectType::Transformation type = ObjectType::Transformation::Global) = delete; + #endif + + private: + inline void draw(const MatrixType&, CameraType*) {} +}; + +/** @brief Two-dimensional scene */ +typedef Scene Scene2D; + +/** @brief Three-dimensional scene */ +typedef Scene Scene3D; + +}} + +#endif diff --git a/src/SceneGraph/Test/CMakeLists.txt b/src/SceneGraph/Test/CMakeLists.txt new file mode 100644 index 000000000..e0db0910d --- /dev/null +++ b/src/SceneGraph/Test/CMakeLists.txt @@ -0,0 +1,3 @@ +corrade_add_test2(SceneGraphObjectTest ObjectTest.cpp LIBRARIES MagnumSceneGraphTestLib) +corrade_add_test2(SceneGraphCameraTest CameraTest.cpp LIBRARIES MagnumSceneGraph) +corrade_add_test2(SceneGraphSceneTest SceneTest.cpp LIBRARIES MagnumSceneGraph) diff --git a/src/SceneGraph/Test/CameraTest.cpp b/src/SceneGraph/Test/CameraTest.cpp new file mode 100644 index 000000000..40dbcfa32 --- /dev/null +++ b/src/SceneGraph/Test/CameraTest.cpp @@ -0,0 +1,138 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "CameraTest.h" + +#include "SceneGraph/Camera.h" + +CORRADE_TEST_MAIN(Magnum::SceneGraph::Test::CameraTest) + +namespace Magnum { namespace SceneGraph { namespace Test { + +CameraTest::CameraTest() { + addTests(&CameraTest::fixAspectRatio, + &CameraTest::defaultProjection2D, + &CameraTest::defaultProjection3D, + &CameraTest::projection2D, + &CameraTest::orthographic, + &CameraTest::perspective); +} + +void CameraTest::fixAspectRatio() { + Vector2 projectionScale(0.5f, 1.0f/3.0f); + Math::Vector2 size(400, 300); + + /* Division by zero */ + Vector2 projectionScaleZeroY(0.5f, 0.0f); + Vector2 projectionScaleZeroX(0.0f, 0.5f); + Math::Vector2 sizeZeroY(400, 0); + Math::Vector2 sizeZeroX(0, 300); + CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Clip, projectionScaleZeroX, size), Matrix4()); + CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Clip, projectionScaleZeroY, size), Matrix4()); + CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Clip, projectionScale, sizeZeroY), Matrix4()); + CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Extend, projectionScale, sizeZeroX), Matrix4()); + + /* Not preserved */ + CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::NotPreserved, projectionScale, size), Matrix4()); + + /* Clip */ + Matrix4 expectedClip(1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 4.0f/3.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Clip, Vector2(0.5f), size), expectedClip); + Matrix4 expectedClipRectangle(1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 2.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Clip, projectionScale, size), expectedClipRectangle); + + /* Extend */ + Matrix4 expectedExtend(3.0f/4.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Extend, Vector2(0.5f), size), expectedExtend); + Matrix4 expectedExtendRectangle(0.5f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Extend, projectionScale, size), expectedExtendRectangle); +} + +void CameraTest::defaultProjection2D() { + Camera2D camera; + CORRADE_COMPARE(camera.projectionMatrix(), Matrix3()); + CORRADE_COMPARE(camera.projectionSize(), Vector2(2.0f)); +} + +void CameraTest::defaultProjection3D() { + Camera3D camera; + CORRADE_COMPARE(camera.projectionMatrix(), Matrix4()); + CORRADE_COMPARE(camera.projectionSize(), Vector2(2.0f)); +} + +void CameraTest::projection2D() { + Vector2 projectionSize(4.0f, 3.0f); + Camera2D camera; + camera.setProjection(projectionSize); + + Matrix3 a(2.0f/4.0f, 0.0f, 0.0f, + 0.0f, 2.0f/3.0f, 0.0f, + 0.0f, 0.0f, 1.0f); + + CORRADE_COMPARE(camera.projectionMatrix(), a); + CORRADE_COMPARE(camera.projectionSize(), projectionSize); +} + +void CameraTest::orthographic() { + Vector2 projectionSize(5); + Camera3D camera; + camera.setOrthographic(projectionSize, 1, 9); + + Matrix4 a(0.4f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.4f, 0.0f, 0.0f, + 0.0f, 0.0f, -0.25f, 0.0f, + 0.0f, 0.0f, -1.25f, 1.0f); + + CORRADE_COMPARE(camera.projectionMatrix(), a); + CORRADE_COMPARE(camera.projectionSize(), projectionSize); + + Vector2 projectionSizeRectangle(5.0f, 4.0f); + camera.setOrthographic(projectionSizeRectangle, 1, 9); + + Matrix4 rectangle(0.4f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.5f, 0.0f, 0.0f, + 0.0f, 0.0f, -0.25f, 0.0f, + 0.0f, 0.0f, -1.25f, 1.0f); + + CORRADE_COMPARE(camera.projectionMatrix(), rectangle); + CORRADE_COMPARE(camera.projectionSize(), projectionSizeRectangle); +} + +void CameraTest::perspective() { + Camera3D camera; + camera.setPerspective(deg(27.0f), 32.0f, 100); + + Matrix4 a(4.1652994f, 0.0f, 0.0f, 0.0f, + 0.0f, 4.1652994f, 0.0f, 0.0f, + 0.0f, 0.0f, -1.9411764f, -1.0f, + 0.0f, 0.0f, -94.1176452f, 0.0f); + + CORRADE_COMPARE(camera.projectionMatrix(), a); + CORRADE_COMPARE(camera.projectionSize(), Vector2(0.48015756f)); +} + +}}} diff --git a/src/Test/CameraTest.h b/src/SceneGraph/Test/CameraTest.h similarity index 73% rename from src/Test/CameraTest.h rename to src/SceneGraph/Test/CameraTest.h index 9f5424647..2bf0e93b5 100644 --- a/src/Test/CameraTest.h +++ b/src/SceneGraph/Test/CameraTest.h @@ -1,5 +1,5 @@ -#ifndef Magnum_Test_CameraTest_h -#define Magnum_Test_CameraTest_h +#ifndef Magnum_SceneGraph_Test_CameraTest_h +#define Magnum_SceneGraph_Test_CameraTest_h /* Copyright © 2010, 2011, 2012 Vladimír Vondruš @@ -17,16 +17,20 @@ #include -namespace Magnum { namespace Test { +namespace Magnum { namespace SceneGraph { namespace Test { class CameraTest: public Corrade::TestSuite::Tester { public: CameraTest(); + void fixAspectRatio(); + void defaultProjection2D(); + void defaultProjection3D(); + void projection2D(); void orthographic(); void perspective(); }; -}} +}}} #endif diff --git a/src/Test/ObjectTest.cpp b/src/SceneGraph/Test/ObjectTest.cpp similarity index 83% rename from src/Test/ObjectTest.cpp rename to src/SceneGraph/Test/ObjectTest.cpp index 9e9f30a72..77f5a7afa 100644 --- a/src/Test/ObjectTest.cpp +++ b/src/SceneGraph/Test/ObjectTest.cpp @@ -14,16 +14,16 @@ */ #include "ObjectTest.h" -#include "Scene.h" -#include "Camera.h" +#include "SceneGraph/Camera.h" +#include "SceneGraph/Scene.h" #include using namespace std; -CORRADE_TEST_MAIN(Magnum::Test::ObjectTest) +CORRADE_TEST_MAIN(Magnum::SceneGraph::Test::ObjectTest) -namespace Magnum { namespace Test { +namespace Magnum { namespace SceneGraph { namespace Test { ObjectTest::ObjectTest() { addTests(&ObjectTest::parenting, @@ -35,13 +35,16 @@ ObjectTest::ObjectTest() { } void ObjectTest::parenting() { - Object root; + Object3D root; - Object* childOne = new Object(&root); - Object* childTwo = new Object(&root); + Object3D* childOne = new Object3D(&root); + Object3D* childTwo = new Object3D(&root); CORRADE_VERIFY(childOne->parent() == &root); - CORRADE_COMPARE(root.children().size(), 2); + CORRADE_VERIFY(childTwo->parent() == &root); + CORRADE_VERIFY(root.firstChild() == childOne); + CORRADE_VERIFY(root.lastChild() == childTwo); + CORRADE_VERIFY(root.firstChild()->nextSibling() == root.lastChild()); /* A object cannot be parent of itself */ childOne->setParent(childOne); @@ -53,17 +56,17 @@ void ObjectTest::parenting() { /* Reparent to another */ childTwo->setParent(childOne); - CORRADE_VERIFY(root.children().size() == 1 && *root.children().begin() == childOne); - CORRADE_VERIFY(childOne->children().size() == 1 && *childOne->children().begin() == childTwo); + CORRADE_VERIFY(root.firstChild() == childOne && root.firstChild()->nextSibling() == nullptr); + CORRADE_VERIFY(childOne->firstChild() == childTwo && childOne->firstChild()->nextSibling() == nullptr); /* Delete child */ delete childTwo; - CORRADE_VERIFY(childOne->children().empty()); + CORRADE_VERIFY(!childOne->hasChildren()); } void ObjectTest::transformation() { - Object o; - Object o2; + Object3D o; + Object3D o2; o.setTransformation(Matrix4::translation(Vector3::xAxis(1.0f))); o2.translate(Vector3::xAxis(1.0f)); @@ -74,8 +77,8 @@ void ObjectTest::transformation() { Matrix4::translation(Vector3::xAxis(1.0f))); CORRADE_COMPARE(o2.transformation(), o.transformation()); - o.multiplyTransformation(Matrix4::scaling(Vector3(2.0f)), Object::Transformation::Local); - o2.scale(Vector3(2.0f), Object::Transformation::Local); + o.multiplyTransformation(Matrix4::scaling(Vector3(2.0f)), Object3D::Transformation::Local); + o2.scale(Vector3(2.0f), Object3D::Transformation::Local); CORRADE_COMPARE(o.transformation(), Matrix4::rotation(deg(35.0f), Vector3::zAxis())* Matrix4::translation(Vector3::xAxis(1.0f))* Matrix4::scaling(Vector3(2.0f))); @@ -86,56 +89,56 @@ void ObjectTest::absoluteTransformationWrongCamera() { stringstream ss; Error::setOutput(&ss); - Scene s; - Object o(&s); + Scene3D s; + Object3D o(&s); o.translate(Vector3::yAxis()); - Camera c; + Camera3D c; CORRADE_COMPARE(o.absoluteTransformation(&c), Matrix4::translation(Vector3::yAxis())); CORRADE_COMPARE(ss.str(), "Object::absoluteTransformation(): the camera is not part of the same scene as object!\n"); ss.str(""); - Object o2; + Object3D o2; o2.translate(Vector3::xAxis()); CORRADE_COMPARE(o2.absoluteTransformation(&c), Matrix4::translation(Vector3::xAxis())); CORRADE_COMPARE(ss.str(), "Object::absoluteTransformation(): the object is not part of camera scene!\n"); } void ObjectTest::absoluteTransformation() { - Scene s; - Camera c(&s); + Scene3D s; + Camera3D c(&s); c.translate(Vector3::zAxis(2.0f)); CORRADE_COMPARE(s.absoluteTransformation(), Matrix4()); CORRADE_COMPARE(c.absoluteTransformation(&c), Matrix4()); - Object o(&s); + Object3D o(&s); o.scale(Vector3(2.0f)); - Object o2(&o); + Object3D o2(&o); o.rotate(deg(90.0f), Vector3::yAxis()); CORRADE_COMPARE(o2.absoluteTransformation(), Matrix4::scaling(Vector3(2.0f))*Matrix4::rotation(deg(90.0f), Vector3::yAxis())); CORRADE_COMPARE(o2.absoluteTransformation(&c), (Matrix4::translation(Vector3::zAxis(2.0f)).inverted())*Matrix4::scaling(Vector3(2.0f))*Matrix4::rotation(deg(90.0f), Vector3::yAxis())); - Object o3; + Object3D o3; o3.translate({1.0f, 2.0f, 3.0f}); CORRADE_COMPARE(o3.absoluteTransformation(), Matrix4::translation({1.0f, 2.0f, 3.0f})); } void ObjectTest::scene() { - Scene scene; + Scene3D scene; - Object* childOne = new Object(&scene); - Object* childTwo = new Object(childOne); + Object3D* childOne = new Object3D(&scene); + Object3D* childTwo = new Object3D(childOne); - Object orphan; - Object* childOfOrphan = new Object(&orphan); + Object3D orphan; + Object3D* childOfOrphan = new Object3D(&orphan); CORRADE_VERIFY(childTwo->scene() == &scene); CORRADE_VERIFY(childOfOrphan->scene() == nullptr); } void ObjectTest::dirty() { - Scene scene; + Scene3D scene; CleaningObject* childOne = new CleaningObject(&scene); childOne->scale(Vector3(2.0f)); @@ -195,4 +198,4 @@ void ObjectTest::dirty() { CORRADE_VERIFY(childThree->isDirty()); } -}} +}}} diff --git a/src/Test/ObjectTest.h b/src/SceneGraph/Test/ObjectTest.h similarity index 76% rename from src/Test/ObjectTest.h rename to src/SceneGraph/Test/ObjectTest.h index da48eabab..01149310f 100644 --- a/src/Test/ObjectTest.h +++ b/src/SceneGraph/Test/ObjectTest.h @@ -1,5 +1,5 @@ -#ifndef Magnum_Test_ObjectTest_h -#define Magnum_Test_ObjectTest_h +#ifndef Magnum_SceneGraph_Test_ObjectTest_h +#define Magnum_SceneGraph_Test_ObjectTest_h /* Copyright © 2010, 2011, 2012 Vladimír Vondruš @@ -17,9 +17,9 @@ #include -#include "Object.h" +#include "SceneGraph/Object.h" -namespace Magnum { namespace Test { +namespace Magnum { namespace SceneGraph { namespace Test { class ObjectTest: public Corrade::TestSuite::Tester { public: @@ -33,12 +33,12 @@ class ObjectTest: public Corrade::TestSuite::Tester { void dirty(); private: - class CleaningObject: public Object { + class CleaningObject: public Object3D { public: - CleaningObject(Object* parent = nullptr): Object(parent) {} + CleaningObject(Object3D* parent = nullptr): Object3D(parent) {} inline void clean(const Matrix4& absoluteTransformation) { - Object::clean(absoluteTransformation); + Object3D::clean(absoluteTransformation); cleanedAbsoluteTransformation = absoluteTransformation; } @@ -46,6 +46,6 @@ class ObjectTest: public Corrade::TestSuite::Tester { }; }; -}} +}}} #endif diff --git a/src/Test/SceneTest.cpp b/src/SceneGraph/Test/SceneTest.cpp similarity index 68% rename from src/Test/SceneTest.cpp rename to src/SceneGraph/Test/SceneTest.cpp index ac05d18e8..65bff5daa 100644 --- a/src/Test/SceneTest.cpp +++ b/src/SceneGraph/Test/SceneTest.cpp @@ -15,11 +15,11 @@ #include "SceneTest.h" -#include "Scene.h" +#include "SceneGraph/Scene.h" -CORRADE_TEST_MAIN(Magnum::Test::SceneTest) +CORRADE_TEST_MAIN(Magnum::SceneGraph::Test::SceneTest) -namespace Magnum { namespace Test { +namespace Magnum { namespace SceneGraph { namespace Test { SceneTest::SceneTest() { addTests(&SceneTest::transformation, @@ -27,25 +27,24 @@ SceneTest::SceneTest() { } void SceneTest::transformation() { - Scene scene; + Scene3D scene; - Object* scenePointer = &scene; + Object3D* scenePointer = &scene; scenePointer->setTransformation(Matrix4::translation({1.0f, 1.0f, 1.0f})); CORRADE_COMPARE(scene.transformation(), Matrix4()); } void SceneTest::parent() { - Scene scene; - - CORRADE_VERIFY(scene.parent() == &scene); + Scene3D scene; + CORRADE_VERIFY(scene.isScene()); /* Scene parent cannot be changed */ - Object* scenePointer = &scene; - Object object; + Object3D* scenePointer = &scene; + Object3D object; scenePointer->setParent(&object); - CORRADE_VERIFY(scene.parent() == &scene); - CORRADE_VERIFY(scene.children().empty()); - CORRADE_VERIFY(object.children().empty()); + CORRADE_VERIFY(scene.parent() == nullptr); + CORRADE_VERIFY(!scene.hasChildren()); + CORRADE_VERIFY(!object.hasChildren()); } -}} +}}} diff --git a/src/Test/SceneTest.h b/src/SceneGraph/Test/SceneTest.h similarity index 83% rename from src/Test/SceneTest.h rename to src/SceneGraph/Test/SceneTest.h index 77af6179d..6d1af5c9b 100644 --- a/src/Test/SceneTest.h +++ b/src/SceneGraph/Test/SceneTest.h @@ -1,5 +1,5 @@ -#ifndef Magnum_Test_SceneTest_h -#define Magnum_Test_SceneTest_h +#ifndef Magnum_SceneGraph_Test_SceneTest_h +#define Magnum_SceneGraph_Test_SceneTest_h /* Copyright © 2010, 2011, 2012 Vladimír Vondruš @@ -17,7 +17,7 @@ #include -namespace Magnum { namespace Test { +namespace Magnum { namespace SceneGraph { namespace Test { class SceneTest: public Corrade::TestSuite::Tester { public: @@ -27,6 +27,6 @@ class SceneTest: public Corrade::TestSuite::Tester { void parent(); }; -}} +}}} #endif diff --git a/src/SceneGraph/magnumSceneGraphVisibility.h b/src/SceneGraph/magnumSceneGraphVisibility.h new file mode 100644 index 000000000..2e1c2e758 --- /dev/null +++ b/src/SceneGraph/magnumSceneGraphVisibility.h @@ -0,0 +1,30 @@ +#ifndef Magnum_SceneGraph_magnumSceneGraphVisibility_h +#define Magnum_SceneGraph_magnumSceneGraphVisibility_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#ifdef _WIN32 + #if defined(MagnumSceneGraph_EXPORTS) || defined(MagnumSceneGraphObjects_EXPORTS) + #define SCENEGRAPH_EXPORT __declspec(dllexport) + #else + #define SCENEGRAPH_EXPORT __declspec(dllimport) + #endif + #define SCENEGRAPH_LOCAL +#else + #define SCENEGRAPH_EXPORT __attribute__ ((visibility ("default"))) + #define SCENEGRAPH_LOCAL __attribute__ ((visibility ("hidden"))) +#endif + +#endif diff --git a/src/Shader.h b/src/Shader.h index 4b5639604..cdb19b456 100644 --- a/src/Shader.h +++ b/src/Shader.h @@ -26,7 +26,7 @@ namespace Magnum { -/** @ingroup rendering +/** * @brief %Shader * * Allows loading and compiling the shader from file or directly from source diff --git a/src/Shaders/PhongShader.h b/src/Shaders/PhongShader.h index 119676cc4..ceb79af6b 100644 --- a/src/Shaders/PhongShader.h +++ b/src/Shaders/PhongShader.h @@ -24,7 +24,7 @@ namespace Magnum { namespace Shaders { -/** @ingroup rendering +/** @brief Phong shader @requires_gl33 The shader is written in GLSL 3.3, although it should be trivial diff --git a/src/SizeTraits.h b/src/SizeTraits.h index 6e6808f2c..0ee09348b 100644 --- a/src/SizeTraits.h +++ b/src/SizeTraits.h @@ -23,10 +23,6 @@ namespace Magnum { -/** @addtogroup utility - * @{ - */ - /** @brief Traits class providing suitable types for given data sizes @tparam byte Highest byte needed (counting from zero) @@ -207,8 +203,6 @@ template struct Log { }; #endif -/*@}*/ - } #endif diff --git a/src/Math/Swizzle.h b/src/Swizzle.h similarity index 71% rename from src/Math/Swizzle.h rename to src/Swizzle.h index e964948a6..22239225b 100644 --- a/src/Math/Swizzle.h +++ b/src/Swizzle.h @@ -1,5 +1,5 @@ -#ifndef Magnum_Math_Swizzle_h -#define Magnum_Math_Swizzle_h +#ifndef Magnum_Swizzle_h +#define Magnum_Swizzle_h /* Copyright © 2010, 2011, 2012 Vladimír Vondruš @@ -16,15 +16,18 @@ */ /** @file - * @brief Function Magnum::Math::swizzle() + * @brief Functions Magnum::swizzle(const T&), Magnum::swizzle(const T&, const char(&)[]) */ -#include "Vector4.h" +#include "Color.h" -namespace Magnum { namespace Math { +namespace Magnum { #ifndef DOXYGEN_GENERATING_OUTPUT namespace Implementation { + using Math::Implementation::Sequence; + using Math::Implementation::GenerateSequence; + template struct GetPosition { static_assert(size > position, "Swizzle parameter out of range of base vector"); @@ -42,12 +45,15 @@ namespace Implementation { template struct GetComponent: public GetPosition {}; template struct TypeForSize { - typedef Vector Type; + typedef Math::Vector Type; }; - - template struct TypeForSize<2, T> { typedef Vector2 Type; }; - template struct TypeForSize<3, T> { typedef Vector3 Type; }; - template struct TypeForSize<4, T> { typedef Vector4 Type; }; + template struct TypeForSize<2, T> { typedef Math::Vector2 Type; }; + template struct TypeForSize<3, T> { typedef Math::Vector3 Type; }; + template struct TypeForSize<4, T> { typedef Math::Vector4 Type; }; + template struct TypeForSize<3, Color3> { typedef Color3 Type; }; + template struct TypeForSize<3, Color4> { typedef Color3 Type; }; + template struct TypeForSize<4, Color3> { typedef Color4 Type; }; + template struct TypeForSize<4, Color4> { typedef Color4 Type; }; inline constexpr size_t getPosition(size_t size, size_t position) { return size > position ? position : throw; @@ -65,7 +71,7 @@ namespace Implementation { throw; } - template inline constexpr Vector swizzleFrom(Sequence, const Vector& vector, const char(&components)[sizeof...(sequence)+1]) { + template inline constexpr Math::Vector swizzleFrom(Sequence, const Math::Vector& vector, const char(&components)[sizeof...(sequence)+1]) { return {vector[getComponent(components[sequence])]...}; } } @@ -87,14 +93,14 @@ two, three or four-component, corresponding Vector2, Vector3 or Vector4 specialization is returned. @attention This function is less convenient to write than -swizzle(const Vector&, const char(&)[newSize]), but the evaluation of +swizzle(const T&, const char(&)[newSize]), but the evaluation of the swizzling operation is guaranteed to be always done at compile time instead of at runtime. @see Vector4::xyz(), Vector4::rgb(), Vector4::xy(), Vector3::xy() */ -template inline constexpr typename Implementation::TypeForSize::Type swizzle(const Vector& vector) { - return {vector[Implementation::GetComponent::value()]...}; +template inline constexpr typename Implementation::TypeForSize::Type swizzle(const T& vector) { + return {vector[Implementation::GetComponent::value()]...}; } /** @@ -113,16 +119,16 @@ two, three or four-component, corresponding Vector2, Vector3 or Vector4 specialization is returned. @attention This function is more convenient to write than -swizzle(const Vector&), but unless the result is marked with +swizzle(const T&), but unless the result is marked with `constexpr`, the evaluation of the swizzling operation probably won't be evaluated at compile time, but at runtime. @see Vector4::xyz(), Vector4::rgb(), Vector4::xy(), Vector3::xy() */ -template inline constexpr typename Implementation::TypeForSize::Type swizzle(const Vector& vector, const char(&components)[newSize]) { +template inline constexpr typename Implementation::TypeForSize::Type swizzle(const T& vector, const char(&components)[newSize]) { return Implementation::swizzleFrom(typename Implementation::GenerateSequence::Type(), vector, components); } -}} +} #endif diff --git a/src/Test/CMakeLists.txt b/src/Test/CMakeLists.txt index 6c20f9a69..f86d239e8 100644 --- a/src/Test/CMakeLists.txt +++ b/src/Test/CMakeLists.txt @@ -1,5 +1,2 @@ -corrade_add_test2(ObjectTest ObjectTest.cpp LIBRARIES MagnumTestLib) -corrade_add_test2(CameraTest CameraTest.cpp LIBRARIES Magnum) -corrade_add_test2(SceneTest SceneTest.cpp LIBRARIES Magnum) - corrade_add_test2(ColorTest ColorTest.cpp) +corrade_add_test2(SwizzleTest SwizzleTest.cpp) diff --git a/src/Test/CameraTest.cpp b/src/Test/CameraTest.cpp deleted file mode 100644 index 4211d0235..000000000 --- a/src/Test/CameraTest.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - Copyright © 2010, 2011, 2012 Vladimír Vondruš - - This file is part of Magnum. - - Magnum is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License version 3 - only, as published by the Free Software Foundation. - - Magnum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License version 3 for more details. -*/ - -#include "CameraTest.h" - -#include "Camera.h" -#include "Scene.h" - -CORRADE_TEST_MAIN(Magnum::Test::CameraTest) - -namespace Magnum { namespace Test { - -CameraTest::CameraTest() { - addTests(&CameraTest::orthographic, - &CameraTest::perspective); -} - -void CameraTest::orthographic() { - Camera camera; - camera.setOrthographic(5, 1, 9); - - Matrix4 a(0.4f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.4f, 0.0f, 0.0f, - 0.0f, 0.0f, -0.25f, 0.0f, - 0.0f, 0.0f, -1.25f, 1.0f); - - CORRADE_COMPARE(camera.projectionMatrix(), a); -} - -void CameraTest::perspective() { - Camera camera; - camera.setPerspective(deg(27.0f), 32.0f, 100); - - Matrix4 a(4.1652994f, 0.0f, 0.0f, 0.0f, - 0.0f, 4.1652994f, 0.0f, 0.0f, - 0.0f, 0.0f, -1.9411764f, -1.0f, - 0.0f, 0.0f, -94.1176452f, 0.0f); - - CORRADE_COMPARE(camera.projectionMatrix(), a); -} - -}} diff --git a/src/Test/ColorTest.cpp b/src/Test/ColorTest.cpp index a500ffee4..7813b5f06 100644 --- a/src/Test/ColorTest.cpp +++ b/src/Test/ColorTest.cpp @@ -21,11 +21,14 @@ using namespace std; CORRADE_TEST_MAIN(Magnum::Test::ColorTest) +using namespace Corrade::Utility; + namespace Magnum { namespace Test { typedef Magnum::Color3 Color3; typedef Magnum::Color4 Color4; typedef Magnum::Color3 Color3f; +typedef Magnum::Color4 Color4f; ColorTest::ColorTest() { addTests(&ColorTest::fromDenormalized, @@ -41,7 +44,10 @@ ColorTest::ColorTest() { &ColorTest::hsv, &ColorTest::hsvOverflow, - &ColorTest::hsvAlpha); + &ColorTest::hsvAlpha, + + &ColorTest::debug, + &ColorTest::configuration); } void ColorTest::fromDenormalized() { @@ -118,4 +124,26 @@ void ColorTest::hsvAlpha() { CORRADE_COMPARE(Color4::fromHSV(230.0f, 0.749f, 0.427f, 23), Color4(27, 41, 109, 23)); } +void ColorTest::debug() { + ostringstream o; + Debug(&o) << Color3f(0.5f, 0.75f, 1.0f); + CORRADE_COMPARE(o.str(), "Vector(0.5, 0.75, 1)\n"); + + o.str(""); + Debug(&o) << Color4f(0.5f, 0.75f, 0.0f, 1.0f); + CORRADE_COMPARE(o.str(), "Vector(0.5, 0.75, 0, 1)\n"); +} + +void ColorTest::configuration() { + Color3f color3(0.5f, 0.75f, 1.0f); + string value3("0.5 0.75 1"); + CORRADE_COMPARE(ConfigurationValue::toString(color3), value3); + CORRADE_COMPARE(ConfigurationValue::fromString(value3), color3); + + Color4f color4(0.5f, 0.75f, 0.0f, 1.0f); + string value4("0.5 0.75 0 1"); + CORRADE_COMPARE(ConfigurationValue::toString(color4), value4); + CORRADE_COMPARE(ConfigurationValue::fromString(value4), color4); +} + }} diff --git a/src/Test/ColorTest.h b/src/Test/ColorTest.h index 068e36da6..47b18101d 100644 --- a/src/Test/ColorTest.h +++ b/src/Test/ColorTest.h @@ -37,6 +37,9 @@ class ColorTest: public Corrade::TestSuite::Tester { void hsv(); void hsvOverflow(); void hsvAlpha(); + + void debug(); + void configuration(); }; }} diff --git a/src/Math/Test/SwizzleTest.cpp b/src/Test/SwizzleTest.cpp similarity index 71% rename from src/Math/Test/SwizzleTest.cpp rename to src/Test/SwizzleTest.cpp index d7ad910c0..765bdb9eb 100644 --- a/src/Math/Test/SwizzleTest.cpp +++ b/src/Test/SwizzleTest.cpp @@ -19,9 +19,9 @@ using namespace std; -CORRADE_TEST_MAIN(Magnum::Math::Test::SwizzleTest) +CORRADE_TEST_MAIN(Magnum::Test::SwizzleTest) -namespace Magnum { namespace Math { namespace Test { +namespace Magnum { namespace Test { typedef Math::Vector2 Vector2; typedef Math::Vector3 Vector3; @@ -66,18 +66,29 @@ void SwizzleTest::type() { CORRADE_VERIFY((is_same::value)); CORRADE_VERIFY((is_same(orig)), Vector4>::value)); CORRADE_VERIFY((is_same::value)); + + Color3 origColor3; + Color4 origColor4; + CORRADE_VERIFY((is_same(origColor3)), Color3>::value)); + CORRADE_VERIFY((is_same(origColor4)), Color3>::value)); + CORRADE_VERIFY((is_same>::value)); + CORRADE_VERIFY((is_same>::value)); + CORRADE_VERIFY((is_same(origColor3)), Color4>::value)); + CORRADE_VERIFY((is_same(origColor4)), Color4>::value)); + CORRADE_VERIFY((is_same>::value)); + CORRADE_VERIFY((is_same>::value)); } void SwizzleTest::defaultType() { Vector4 orig(1, 2, 3, 4); - Vector<1, int> b(3); + Math::Vector<1, int> b(3); CORRADE_COMPARE(swizzle<'b'>(orig), b); CORRADE_COMPARE(swizzle(orig, "b"), b); - Vector<7, int> bragzyx(3, 1, 4, 2, 3, 2, 1); + Math::Vector<7, int> bragzyx(3, 1, 4, 2, 3, 2, 1); CORRADE_COMPARE((swizzle<'b', 'r', 'a', 'g', 'z', 'y', 'x'>(orig)), bragzyx); CORRADE_COMPARE(swizzle(orig, "bragzyx"), bragzyx); } -}}} +}} diff --git a/src/Math/Test/SwizzleTest.h b/src/Test/SwizzleTest.h similarity index 86% rename from src/Math/Test/SwizzleTest.h rename to src/Test/SwizzleTest.h index a08dfdd17..b43180be0 100644 --- a/src/Math/Test/SwizzleTest.h +++ b/src/Test/SwizzleTest.h @@ -1,5 +1,5 @@ -#ifndef Magnum_Math_Test_SwizzleTest_h -#define Magnum_Math_Test_SwizzleTest_h +#ifndef Magnum_Test_SwizzleTest_h +#define Magnum_Test_SwizzleTest_h /* Copyright © 2010, 2011, 2012 Vladimír Vondruš @@ -17,7 +17,7 @@ #include -namespace Magnum { namespace Math { namespace Test { +namespace Magnum { namespace Test { class SwizzleTest: public Corrade::TestSuite::Tester { public: @@ -30,6 +30,6 @@ class SwizzleTest: public Corrade::TestSuite::Tester { void defaultType(); }; -}}} +}} #endif diff --git a/src/Texture.h b/src/Texture.h index b36bfa049..d31d2039b 100644 --- a/src/Texture.h +++ b/src/Texture.h @@ -16,17 +16,13 @@ */ /** @file - * @brief Class Magnum::Texture + * @brief Class Magnum::Texture, typedef Magnum::Texture1D, Magnum::Texture2D, Magnum::Texture3D */ #include "AbstractTexture.h" namespace Magnum { -/** @addtogroup textures - * @{ - */ - /** @brief %Texture @@ -189,8 +185,6 @@ typedef Texture<2> Texture2D; typedef Texture<3> Texture3D; #endif -/*@}*/ - } #endif diff --git a/src/Trade/AbstractImporter.h b/src/Trade/AbstractImporter.h index 03f3d0729..2b7ed2528 100644 --- a/src/Trade/AbstractImporter.h +++ b/src/Trade/AbstractImporter.h @@ -19,7 +19,7 @@ * @brief Class Magnum::Trade::AbstractImporter */ -#include +#include #include #include "ImageData.h" @@ -65,7 +65,7 @@ class MAGNUM_EXPORT AbstractImporter: public Corrade::PluginManager::Plugin { }; /** @brief Set of features supported by this importer */ - typedef Corrade::Utility::Set Features; + typedef Corrade::Containers::EnumSet Features; /** @brief Constructor */ inline AbstractImporter(Corrade::PluginManager::AbstractPluginManager* manager = nullptr, const std::string& plugin = ""): Plugin(manager, plugin) {} @@ -310,7 +310,7 @@ class MAGNUM_EXPORT AbstractImporter: public Corrade::PluginManager::Plugin { /*@}*/ }; -SET_OPERATORS(AbstractImporter::Features) +CORRADE_ENUMSET_OPERATORS(AbstractImporter::Features) /* Implementations for inline functions with unused parameters */ inline int AbstractImporter::sceneForName(const std::string&) { return -1; } diff --git a/src/TypeTraits.h b/src/TypeTraits.h index 31dd42f93..3e95eec12 100644 --- a/src/TypeTraits.h +++ b/src/TypeTraits.h @@ -23,10 +23,6 @@ namespace Magnum { -/** @addtogroup utility - * @{ - */ - /** @brief Traits class for plain OpenGL types @@ -148,8 +144,6 @@ struct MAGNUM_EXPORT TypeInfo { /** @todo Other texture types, referenced in glTexImage2D function manual */ /** @todo Using Vector3 for textures? */ -/*@}*/ - #ifndef DOXYGEN_GENERATING_OUTPUT template<> struct TypeOf { typedef GLubyte Type; }; template<> struct TypeOf { typedef GLbyte Type; };