diff --git a/doc/changelog.dox b/doc/changelog.dox
index 55a855c53..a4a315ac1 100644
--- a/doc/changelog.dox
+++ b/doc/changelog.dox
@@ -51,6 +51,8 @@ See also:
including mapping to @ref GL::PixelFormat / @ref GL::PixelType,
@ref GL::TextureFormat and @ref Vk::PixelFormat and (partial) support in
@ref DebugTools::CompareImage
+- New @ref Degh, @ref Radh, @ref Range1Dh, @ref Range2Dh and @ref Range3Dh
+ typedefs for half-float angles and ranges
@subsubsection changelog-latest-new-debugtools DebugTools library
@@ -734,6 +736,10 @@ See also:
reflected in the docs and thus hidden from users. Now it's shown in the
docs as a set of @ref Debug etc typedefs instead, to make them more
discoverable.
+- Improved documentation about @ref opengl-wrapping-instances-nocreate "delayed OpenGL context creation"
+ and related pitfalls (see also [mosra/magnum-bootstrap#26](https://github.com/mosra/magnum-bootstrap/issues/26))
+- Added introductory documentation about @ref features "library layout and essential workflows"
+ (see [mosra/magnum#526](https://github.com/mosra/magnum/issues/526))
@section changelog-2020-06 2020.06
diff --git a/doc/compilation-speedup.dox b/doc/compilation-speedup.dox
index 688401977..53dbba44e 100644
--- a/doc/compilation-speedup.dox
+++ b/doc/compilation-speedup.dox
@@ -25,27 +25,27 @@
namespace Magnum {
/** @page compilation-speedup Speeding up compilation
-@brief Techniques for reducing compilation times.
+@brief Techniques for reducing compilation times used by Magnum itself and recommended for application code as well.
@tableofcontents
@m_footernavigation
@section compilation-forward-declarations Forward declarations instead of includes
-Essential thing when speeding up compilation is reducing number of @cpp #include @ce
-directives in both headers and source files. Magnum is strictly applying this
-policy in all header files, so all types which are not directly used in the
-header have only forward declarations.
+An essential thing when speeding up compilation is reducing number of
+@cpp #include @ce directives in both headers and source files. Magnum is
+strictly applying this policy in all header files, so all types which are not
+directly used in the header have only forward declarations.
For example, when including @ref Magnum.h, you get shortcut typedefs for
floating-point vectors and matrices like @ref Vector3 and @ref Matrix4, but to
actually use any of them, you have to include the respective header, e.g.
@ref Magnum/Math/Vector3.h.
-You are encouraged to use forward declarations also in your code. However, for
-some types it can be too cumbersome --- e.g. too many template parameters,
-typedefs etc. In this case a header with forward declarations is usually
-available, each namespace has its own:
+You are encouraged to use forward declarations in your code as well. However,
+for some types it can be too cumbersome --- e.g. too many template parameters,
+default template arguments, typedefs etc. Instead, forward declaration headers
+are available, with each namespace having its own:
- @ref Corrade/Corrade.h
- @ref Corrade/Containers/Containers.h
diff --git a/doc/features.dox b/doc/features.dox
index eb8eccef4..e9328c959 100644
--- a/doc/features.dox
+++ b/doc/features.dox
@@ -26,10 +26,146 @@
namespace Magnum {
/** @page features Feature guide
-@brief Fundamental principles and design goals.
+@brief High-level introduction to design of the Magnum library and basic building blocks.
-Overview and tutorials of high-level feature groups in Magnum. It's not
-necessary to read through everything, pick only what you need.
+@tableofcontents
+
+@todoc FUCK OFF doxygen, why are you not able to link to Corrade, Corrade::Utility and Magnum/Math/ you dumb shit?!
+
+Before you continue further, here are the essential bits of knowledge to help
+you around.
+
+@section features-naming Library organization and naming scheme
+
+The @ref Magnum project consists of a library with core functionality and
+optional sub-libraries. Each library is in its own namespace, which corresponds
+to a sub-folder on the include path -- so e.g. things from @ref Magnum::Math
+are included from @m_class{m-doc} [Magnum/Math/](dir_d816e7cf853e6723911731706bcab386.html).
+@ref Magnum builds upon
+@m_class{m-doc-external} [Corrade](https://doc.magnum.graphics/corrade/), which
+provides platform abstraction, basic utilities and containers.
+
+To reduce redundant verbosity, references throughout the documentation and
+code in example snippets have the the root @ref Magnum and
+@m_class{m-doc-external} [Corrade](https://doc.magnum.graphics/corrade/namespaceCorrade.html)
+namespaces omitted. In other words, as if the following was done:
+
+@snippet Magnum.cpp features-using-namespace
+
+If Magnum is the primary library you're building your project on, it's
+recommended that you do the same --- we're careful to not pollute the root
+namespace with overly generic names. However, except for `Literals` namespaces,
+@cpp using @ce the subnamespaces is *not recommended*, as naming in those is
+deliberately picked short and may introduce conflicts (such as
+@relativeref{Corrade,Containers::String} vs
+@relativeref{Corrade,Utility::String} or @ref GL::PixelFormat vs
+@ref Vk::PixelFormat).
+
+If you frown upon the thought of @cpp using namespace Magnum @ce yet don't want
+to wear your fingers off by repeatedly writing @cpp Magnum:: @ce everywhere, a
+common shorthand that projects go with is the following --- similarly in spirit
+to @cb{.py} import numpy as np @ce:
+
+@snippet Magnum.cpp features-using-namespace-alias
+
+@section features-includes Include files and forward declarations
+
+Depending on where you come from, the @cpp #include @ce policy used by Magnum
+might either make you happy or freak you out. In short, there's no "include the
+world" file, and instead you're supposed to include dedicated headers for APIs
+you want to use. The main reason is compile times, and the speed gain from
+doing it this way is too great to be ignored.
+
+The general rule is that each top-level class has a corresponding include, so
+for example @ref Math::Matrix4 is included as @ref Magnum/Math/Matrix4.h. It's
+not always like that though, and to help you out, documentation of each
+namespace and class tells you what to include, and if particular namespace
+members are defined in different headers, then detailed docs of each has a
+corresponding @cpp #include @ce directive listed as well, ready to be copied.
+
+In order to have the includes actually independent of each other, most Magnum
+types are forward-declared, and where possible, the header only relies on
+forward declarations. Which means that often the type already exists as a
+declaration, and in order to actually use it, you have to include the concrete
+definition of it. If you don't, the compiler will complain about use of an
+incomplete type. For example:
+
+@snippet Magnum.cpp features-forward-declaration-use
+
+Of course not all headers can be written with just forward declarations, so
+there still are some transitive dependencies between headers (for example, the
+@ref Magnum/Math/Matrix4.h header pulls in @ref Magnum/Math/Vector4.h as well).
+But from most part this is an implementation detail and as such shouldn't be
+relied on.
+
+For happy compile times you're encouraged to rely on forward declarations in
+your code as well. See @ref compilation-forward-declarations for more
+information.
+
+@section features-debug-output Debug output
+
+One of the essential debugging workflows is inspection of variable contents by
+printing them out. Magnum defines a lot of new math types, enums and
+containers and it would be very painful if you had to loop over their contents
+or perform manual enum-to-string conversion every time you want to see what's
+inside.
+
+Because writing to standard output and printing values for debugging purposes
+are *distinct* use cases with potentially conflicting requirements (should an
+enum value get written as a number? or as a name? a fully qualified name?),
+Magnum *doesn't* provide @cpp operator<< @ce overloads for @ref std::ostream.
+
+Instead, there's @relativeref{Corrade,Utility::Debug}, for your typing
+convenience also aliased to just @ref Debug directly in the @ref Magnum
+namespace. On its own it's able to print builtin types, all usual C++
+containers as well as their Corrade equivalents. Magnum then implements debug
+printers for its own math types, basic structures and most enums, which get
+printed as fully-qualified names. For example:
+
+@m_class{m-code-figure}
+
+@parblock
+
+@snippet Magnum.cpp features-debug-output
+
+
+
+@m_class{m-nopad}
+
+@code{.shell-session}
+Image format is PixelFormat::RGBA8Srgb and size Vector(256, 192)
+Color of the bottom-left pixel is #33b27f
+@endcode
+
+@endparblock
+
+The main goal of this utility is convenience and readability --- values are
+implicitly delimited by spaces and ended with a newline, container contents
+written with commas etc. Check out the class documentation for advanced
+features like colors, output redirection or printing file/line info.
+
+@section features-examples Learn by example
+
+Before you do a deep dive into the documentation, and if you haven't done
+already, it's recommended to go through the Getting Started Guide and check out
+at least the first example:
+
+@m_class{m-row}
+
+@parblock
+
+@m_div{m-col-m-6} @m_div{m-button m-primary} @m_div{m-big}Getting Started@m_enddiv @m_div{m-small} bootstrap a basic project structure @m_enddiv @m_enddiv @m_enddiv
+
+@m_div{m-col-m-6} @m_div{m-button m-success} @m_div{m-big}Your First Triangle@m_enddiv @m_div{m-small} a step-by-step tutorial @m_enddiv @m_enddiv @m_enddiv
+
+@endparblock
+
+@section features-building-blocks Learn through documentation
+
+Each of the following pages provides a high-level description of a certain area
+of the library. It's recommended to read through these first to understand the
+overall principles and only then go to documentation of each concrete class and
+function.
- @subpage platform --- @copybrief platform
- @subpage types --- @copybrief types
diff --git a/doc/getting-started.dox b/doc/getting-started.dox
index 544bc2eb5..6d4f10506 100644
--- a/doc/getting-started.dox
+++ b/doc/getting-started.dox
@@ -345,11 +345,19 @@ See @ref platform for more information.
@section getting-started-tutorials Follow tutorials and learn the principles
Now that you have your first application up and running, the best way to
-continue is to render your first triangle. Then you can dig deeper and try
-other examples, read about @ref features "fundamental principles" in the
-documentation and start experimenting on your own!
+continue is to get familiar with the basic workflows and render your first
+triangle. Then you can dig deeper and try other examples, read about basic
+building blocks in the documentation and start experimenting on your own!
-@m_div{m-button m-success} @m_div{m-big}Your First Triangle@m_enddiv @m_div{m-small} a step-by-step tutorial @m_enddiv @m_enddiv
+@m_class{m-row}
+
+@parblock
+
+@m_div{m-col-m-6} @m_div{m-button m-info} @m_div{m-big}The Fundamentals@m_enddiv @m_div{m-small} make yourself comfortable first @m_enddiv @m_enddiv @m_enddiv
+
+@m_div{m-col-m-6} @m_div{m-button m-success} @m_div{m-big}Your First Triangle@m_enddiv @m_div{m-small} a step-by-step tutorial @m_enddiv @m_enddiv @m_enddiv
+
+@endparblock
@section getting-started-more Additional information
diff --git a/doc/opengl-wrapping.dox b/doc/opengl-wrapping.dox
index 2a3821728..d642ca725 100644
--- a/doc/opengl-wrapping.dox
+++ b/doc/opengl-wrapping.dox
@@ -31,9 +31,9 @@ namespace Magnum {
@m_footernavigation
The purpose of the @ref GL library is to simplify interaction with the OpenGL
-API using type-safe C++11 features, abstracting away extension and platform
-differences, tracking the state for optimum performance and selecting the best
-available code path for given system.
+API using type-safe C++11 features and RAII, abstracting away extension and
+platform differences, tracking the state for optimum performance and selecting
+the best available code path for given driver.
Magnum provides wrappers for most native OpenGL objects like buffers, textures,
meshes, queries, transform feedback objects, shaders etc., but makes it
@@ -42,49 +42,65 @@ libraries if the user wants to.
@section opengl-wrapping-instances OpenGL object wrapper instances
-By default, all underlying OpenGL objects are created in wrapper class
-constructor and deleted in wrapper class destructor. Constructing an object
-using default constructor requires active @ref GL::Context instance. All OpenGL
-objects are movable (but not copyable), although for performance reasons (and
-contrary to standard C++11 practice), the moved-from instance does *not* have
-any associated OpenGL object and is thus in *invalid state*. Using instance in
-moved-from state may result in OpenGL errors being generated, in some cases
-even application crashes.
-
-Besides the default behavior, it is possible to construct the object without
-creating the underlying OpenGL object using the @ref NoCreate tag.
-Constructing the object this way does not require any active context and its
-state is then equivalent to the moved-from state. It is useful in case you need
-to construct the object before creating context (such as class members) or if
-you know you would overwrite it later with another object:
-
-@snippet MagnumGL.cpp opengl-wrapping-nocreate
-
-
-
-@m_class{m-note m-warning}
-
-@par
- Note that calling anything on objects created this way is not defined (and
- not checked or guarded in any way) and may result in crashes. If you want delayed object creation with safety checks (however with some extra
- memory overhead), wrap the objects in an @ref Corrade::Containers::Optional.
+By default, all wrapper classes follow RAII -- underlying OpenGL objects are
+created in the class constructor and deleted in the wrapper class destructor.
+All OpenGL objects are movable (but not copyable) and moves are *destructive*
+--- the moved-from instance does *not* have any associated OpenGL object and
+is thus in an *invalid state*. Calling anything on instances in a moved-from
+state may thus result in OpenGL errors being generated, in some cases even
+application crashes.
If you need to preserve the underlying OpenGL object after destruction, you can
call @cpp release() @ce. It returns ID of the underlying object, the instance
-is then equivalent to moved-from state and you are responsible for proper
+is then equivalent to the moved-from state and you are responsible for proper
deletion of the returned OpenGL object (note that it is possible to just query
-ID of the underlying without releasing it using `id()`). It is also possible to
-do the opposite --- wrapping existing OpenGL object ID into Magnum object
-instance using @cpp wrap() @ce.
+ID of the underlying without releasing it using @cpp id() @ce). It is also
+possible to do the opposite --- wrapping an existing OpenGL object ID into a
+Magnum object instance using @cpp wrap() @ce:
@snippet MagnumGL.cpp opengl-wrapping-transfer
-The @cpp NoCreate @ce constructor, @cpp wrap() @ce and @cpp release() @ce
-functions are available for all OpenGL classes except @ref GL::Shader, where
-wrapping external instances makes less sense.
-
-Note that interaction with third-party OpenGL code as shown above usually needs
-special attention:
+The @cpp wrap() @ce and @cpp release() @ce functions are available for all
+OpenGL classes except for @ref GL::Shader, instances of which are rather
+short-lived and thus wrapping external instances makes less sense.
+
+@attention
+ Note that interaction with third-party OpenGL code as shown above usually
+ needs special attention due to the global nature of the OpenGL state
+ tracker. See @ref opengl-state-tracking below for more information.
+
+@subsection opengl-wrapping-instances-nocreate Delayed context creation and NoCreate constructors
+
+Constructing a Magnum GL object requires an active @ref GL::Context instance.
+By default, this instance is automatically created by
+@ref Platform::Sdl2Application "Platform::*Application" constructors, however
+if you use @ref platform-configuration-delayed "delayed context creation" or
+are directly interfacing with @ref platform-custom "custom platform toolkits",
+this is not the case. If OpenGL functionality gets called before the
+@ref GL::Context instance is created (or after it has been destroyed), you may
+end up with the following assertion:
+
+@code{.shell-session}
+GL::Context::current(): no current context
+@endcode
+
+In the common case of delayed context creation, this problem can be
+circumvented by constructing the OpenGL objects using the @ref NoCreate tag
+first and populating them with live instances once the context is ready. For
+example:
+
+@snippet MagnumGL-application.cpp opengl-wrapping-nocreate
+
+Please note that objects constructed using the @ref NoCreate tag are equivalent
+to the moved-from state, and thus again calling anything on these may result in
+OpenGL errors being generated or even application crashes --- you're
+responsible for correctly initializing the objects before use. If you are fine
+with trading some overhead for extra safety checks, wrap the objects in an
+@relativeref{Corrade,Containers::Optional} instead of using the @ref NoCreate
+constructor.
+
+The @ref NoCreate constructor is available for all OpenGL classes, including
+builtin shaders.
@section opengl-state-tracking State tracking and interaction with third-party code
diff --git a/doc/platform.dox b/doc/platform.dox
index bae1a6483..557c079ae 100644
--- a/doc/platform.dox
+++ b/doc/platform.dox
@@ -143,22 +143,49 @@ this:
@snippet MagnumPlatform.cpp configuration
-However, sometimes you would need to configure the application based on some
-configuration file or system introspection. In that case you can pass
-@ref NoCreate instead of @ref Platform::Sdl2Application::Configuration "Configuration"
-instance and then specify it later with @ref Platform::Sdl2Application::create() "create()":
+@subsection platform-configuration-delayed Delayed context creation
+
+Sometimes you may want to set up the application based on a configuration file
+or system introspection, which can't really be done inside the base class
+initializer. You can specify @ref NoCreate in the constructor instead and pass
+the @relativeref{Platform::Sdl2Application,Configuration} later to a
+@relativeref{Platform::Sdl2Application,create()} function:
@snippet MagnumPlatform.cpp createcontext
-If the context creation in constructor or @ref Platform::Sdl2Application::create() "create()"
-fails, the application exits. However, it is also possible to negotiate the
-context using @ref Platform::Sdl2Application::tryCreate() "tryCreate()". The
-only difference is that this function returns `false` instead of exiting. You
-can for example try enabling MSAA and if the context creation fails, fall back
-to no-AA rendering:
+If context creation in the constructor or in
+@relativeref{Platform::Sdl2Application,create()} fails, the application prints
+an error message to standard output and exits. While that frees you from having
+to do explicit error handling, sometimes a more graceful behavior may be
+desirable --- with @relativeref{Platform::Sdl2Application,tryCreate()} the
+function returns @cpp false @ce instead of exiting and it's up to you whether
+you abort the launch or retry with different configuration. You can for example
+try enabling MSAA first, and if the context creation fails, fall back to no-AA
+rendering:
@snippet MagnumPlatform.cpp trycreatecontext
+
+
+@m_class{m-block m-warning}
+
+@par Implications for GL objects as class members
+ Delaying GL context creation to
+ @relativeref{Platform::Sdl2Application,create()} /
+ @relativeref{Platform::Sdl2Application,tryCreate()} means that, at the
+ point when class members get constructed, the context isn't available yet.
+ If you have GL objects such as @ref GL::Mesh or @ref Shaders::PhongGL as
+ class members, the application may hit the following assert during startup:
+@par
+ @code{.shell-session}
+ GL::Context::current(): no current context
+ @endcode
+@par
+ The solution is to construct the GL objects with @ref NoCreate constructors
+ as well and populate them with live instances only after the context has
+ been created. See @ref opengl-wrapping-instances-nocreate for more
+ information.
+
@section platform-custom Using custom platform toolkits
In case you want to use some not-yet-supported toolkit or you don't want to use
diff --git a/doc/snippets/CMakeLists.txt b/doc/snippets/CMakeLists.txt
index 9af7aca4b..f1c6299e6 100644
--- a/doc/snippets/CMakeLists.txt
+++ b/doc/snippets/CMakeLists.txt
@@ -203,7 +203,7 @@ if(WITH_SDL2APPLICATION AND TARGET_GL)
add_library(snippets-MagnumPlatform STATIC
MagnumPlatform.cpp
- MagnumGL-framebuffer.cpp)
+ MagnumGL-application.cpp)
target_link_libraries(snippets-MagnumPlatform PRIVATE MagnumSdl2Application)
set_target_properties(
diff --git a/doc/snippets/Magnum.cpp b/doc/snippets/Magnum.cpp
index b76710945..67b66c8de 100644
--- a/doc/snippets/Magnum.cpp
+++ b/doc/snippets/Magnum.cpp
@@ -26,6 +26,7 @@
#include
#include "Magnum/Math/Color.h"
+#include "Magnum/Math/Matrix4.h"
#include "Magnum/Image.h"
#include "Magnum/ImageView.h"
#include "Magnum/PixelFormat.h"
@@ -37,6 +38,8 @@
#include "Magnum/GL/Texture.h"
#endif
+#define DOXYGEN_IGNORE(...) __VA_ARGS__
+
using namespace Magnum;
using namespace Magnum::Math::Literals;
@@ -64,6 +67,42 @@ class MeshResourceLoader: public AbstractResourceLoader {
int main() {
+{
+/* [features-using-namespace] */
+using namespace Corrade;
+using namespace Magnum;
+/* [features-using-namespace] */
+}
+
+{
+/* [features-using-namespace-alias] */
+namespace Cr = Corrade;
+namespace Mn = Magnum;
+/* [features-using-namespace-alias] */
+}
+
+{
+/* The same #include is already above so this shouldn't hurt */
+/* [features-forward-declaration-use] */
+#include /* only a Matrix4 forward declaration */
+#include /* the actual definition */
+
+DOXYGEN_IGNORE()
+
+Matrix4 a = Matrix4::translation({3.0f, 1.0f, 0.5f});
+/* [features-forward-declaration-use] */
+static_cast(a);
+}
+
+{
+/* [features-debug-output] */
+Image2D image = DOXYGEN_IGNORE(Image2D{{}, {}, {}});
+
+Debug{} << "Image format is" << image.format() << "and size" << image.size();
+Debug{} << "Color of the first pixel is" << image.pixels()[0][0];
+/* [features-debug-output] */
+}
+
{
std::nullptr_t data{};
/* [Image-pixels] */
diff --git a/doc/snippets/MagnumGL-framebuffer.cpp b/doc/snippets/MagnumGL-application.cpp
similarity index 80%
rename from doc/snippets/MagnumGL-framebuffer.cpp
rename to doc/snippets/MagnumGL-application.cpp
index 70b82db50..e17606270 100644
--- a/doc/snippets/MagnumGL-framebuffer.cpp
+++ b/doc/snippets/MagnumGL-application.cpp
@@ -26,11 +26,38 @@
#include "Magnum/GL/Buffer.h"
#include "Magnum/GL/DefaultFramebuffer.h"
#include "Magnum/GL/Framebuffer.h"
+#include "Magnum/GL/Mesh.h"
#include "Magnum/Platform/Sdl2Application.h"
#include "Magnum/Platform/GLContext.h"
+#include "Magnum/Shaders/PhongGL.h"
using namespace Magnum;
+#define DOXYGEN_IGNORE(...) __VA_ARGS__
+
+/* [opengl-wrapping-nocreate] */
+class MyApplication: public Platform::Application {
+ DOXYGEN_IGNORE(explicit MyApplication(const Arguments& arguments);)
+
+ private:
+ /* Placeholders without an underlying GL object */
+ GL::Mesh _mesh{NoCreate};
+ Shaders::PhongGL _shader{NoCreate};
+ DOXYGEN_IGNORE()
+};
+
+MyApplication::MyApplication(const Arguments& arguments):
+ Platform::Application{arguments, NoCreate}
+{
+ DOXYGEN_IGNORE()
+ create();
+
+ /* GL context is ready, now it's safe to populate the GL objects */
+ _mesh = GL::Mesh{};
+ _shader = Shaders::PhongGL{};
+}
+/* [opengl-wrapping-nocreate] */
+
struct A: Platform::Sdl2Application {
/* [DefaultFramebuffer-usage-viewport] */
void viewportEvent(ViewportEvent& event) override {
diff --git a/doc/snippets/MagnumGL.cpp b/doc/snippets/MagnumGL.cpp
index e11b93612..5876a4c3d 100644
--- a/doc/snippets/MagnumGL.cpp
+++ b/doc/snippets/MagnumGL.cpp
@@ -120,23 +120,6 @@ carBumpTexture.setStorage(5, GL::TextureFormat::RGB8, {256, 256})
}
#endif
-{
-#if defined(CORRADE_TARGET_GCC) && __GNUC__ >= 11
-#pragma GCC diagnostic push
-/* Stupid thing. YES I WANT THIS TO BE A FUNCTION, CAN YOU SHUT UP */
-#pragma GCC diagnostic ignored "-Wvexing-parse"
-#endif
-auto importSomeMesh() -> std::tuple;
-#if defined(CORRADE_TARGET_GCC) && __GNUC__ >= 11
-#pragma GCC diagnostic pop
-#endif
-/* [opengl-wrapping-nocreate] */
-GL::Mesh mesh{NoCreate};
-GL::Buffer vertices{NoCreate}, indices{NoCreate};
-std::tie(mesh, vertices, indices) = importSomeMesh();
-/* [opengl-wrapping-nocreate] */
-}
-
{
struct Foo {
void setSomeBuffer(GLuint) {}
diff --git a/doc/snippets/MagnumPlatform.cpp b/doc/snippets/MagnumPlatform.cpp
index 7da163055..a1f312e97 100644
--- a/doc/snippets/MagnumPlatform.cpp
+++ b/doc/snippets/MagnumPlatform.cpp
@@ -89,9 +89,9 @@ struct MyApplication: Platform::Application {
/* [configuration] */
MyApplication::MyApplication(const Arguments& arguments):
- Platform::Application(arguments, Configuration{}
+ Platform::Application{arguments, Configuration{}
.setTitle("My Application")
- .setSize({800, 600}))
+ .setSize({800, 600})}
{
// ...
}
diff --git a/doc/troubleshooting.dox b/doc/troubleshooting.dox
index 909788932..cc618cc8b 100644
--- a/doc/troubleshooting.dox
+++ b/doc/troubleshooting.dox
@@ -25,29 +25,87 @@
namespace Magnum {
/** @page troubleshooting Troubleshooting
-@brief Various tricks to overcome common building and rendering issues.
+@brief Various tricks and solutions to common pitfalls.
+@tableofcontents
@m_footernavigation
@section troubleshooting-building Building issues
-If your project suddenly stops building after Magnum upgrade, check these
+If your project suddenly stops building after a Magnum upgrade, check these
things:
-- If the building fails on CMake step, be sure that you have up-to-date
+- If building fails on the CMake step, be sure that you have up-to-date
`FindCorrade.cmake`, `FindMagnum.cmake` and other CMake modules in your
- project (`FindSDL2.cmake`). They are contained in `modules/` directory of
- Magnum sources (and sources of other projects) also are installed into
- `share/cmake/Magnum`.
-- In some cases when the changes done to build system are too drastic,
- recreating the build dir or clearing CMake cache is needed, but this is
- a very rare occasion.
-- The library is constantly evolving, thus some API might get deprecated over
- time (and later removed). Either build the libraries with `BUILD_DEPRECATED`
- or switch to non-deprecated features. See @ref building for more
- information.
-
-@section troubleshooting-rendering Rendering issues
+ project (`FindSDL2.cmake`, ...). They are contained in `modules/` directory
+ of Magnum repositories and also get installed into `share/cmake/Magnum`.
+- In very rare cases the CMake build directory may not survive an upgrade. If
+ you get some particularly cursed errors, try recreating it from scratch.
+- The library is constantly evolving, meaning that APIs may get deprecated
+ and removed over time. If you upgrade to a version that deprecated a
+ particular API you use, the code will emit a deprecation warning,
+ suggesting a migration path. Except for rare cases, the deprecated APIs are
+ guaranteed to stay in the codebase for a year at least (or two subsequent
+ version releases, whichever is longer) and only then they get removed. To
+ make sure you're no longer using any deprecated functionality, it's
+ possible to disable the `BUILD_DEPRECATED` @ref building-features "CMake option"
+ when building Magnum, although for a smoother experience it's recommended
+ to flip this option back on when upgrading. A list of deprecated APIs is
+ maintained @ref changelog-latest-deprecated "in the changelog".
+- In some rare cases, it's impossible to provide a deprecated migration path
+ and an API gets changed in a backwards-incompatible manner, directly
+ leading to a compile error. Another possibility is that transitive header
+ dependencies got cleaned up for @ref compilation-forward-declarations "speeding up compilation" and you're now missing an @cpp #include @ce. The major
+ compatiblity breakages are always listed @ref changelog-latest-compatibility "in the changelog".
+- If you upgrade from a really old release, it's recommended to gradually
+ upgrade over tagged versions of Magnum and not jumping directly to latest
+ version. That way you're more likely to follow the deprecation path,
+ instead of directly ending up with long-deprecated APIs being gone with no
+ direct way to know what should be used instead. If in doubt, browse the
+ @ref changelog "old changelogs".
+
+@section troubleshooting-runtime Runtime issues
+
+Magnum makes heavy use of assertions to catch programmer errors (as opposed to
+runtime or data-dependent errors, which get handled in more graceful ways), and
+because past experience showed their usefulness, majority of assertions is
+@ref CORRADE_NO_ASSERT "by default kept even in release builds".
+
+If your application abruptly exits, it's important to know whether it was a
+regular *exit*, an *abort* or a *crash*, as each of these may point to a
+different problem.
+
+- Except for crashes, in which case the application usually won't even have a
+ chance to complain about anything, you should get a message on the standard
+ error output:
+ - On Unix systems the output can be seen when running from a console
+ - On Windows, if you don't see the console, switch the build back to a
+ console app @ref platform-windows-hiding-console "instead of a GUI app"
+ --- in case of CMake you can temporarily remove the `WIN32` part from
+ your @cmake add_executable() @ce call
+ - On Android @ref platforms-android-output-redirection "you can use logcat"
+ - On Emscripten the output is printed to the browser console
+- A regular exit may happen during startup due to an error in arguments
+ passed on the command line, or when a window / context creation fails. The
+ error output should mention what happened.
+- An abort is an assertion failure, with the error message telling you why.
+ It's an abort in order to trigger a debugger break (or the assertion dialog
+ on Windows) or create a core dump (on Unix systems) so you can see the
+ backtrace leading to the error.
+ - One of the more frequent assertion messages is
+ @cb{.shell-session} GL::Context::current(): no current context @ce.
+ This happens when you try to use OpenGL functionality when the OpenGL
+ context is not yet created (or no longer exists). See
+ @ref opengl-wrapping-instances-nocreate for more information.
+- A crash is usually a memory issue. To find the root cause, by far the
+ easiest is to hook up AddressSanitizer (`-fsanitize=address` on GCC, Clang
+ and [recent MSVC](https://docs.microsoft.com/en-us/cpp/sanitizers/asan)).
+ It can detect out-of-bounds accesses, use-after-free, leaks and other
+ issues and upon discovering a problem it prints a lengthy diagnostic about
+ what happened, where does the memory come from and what code touched it and
+ how.
+
+@section troubleshooting-opengl OpenGL issues
If you are experiencing the so-called "black screen of death", weird behavior
or crashes on GL calls, you might want to try these things:
@@ -99,7 +157,7 @@ or crashes on GL calls, you might want to try these things:
having similar ability to reset its state tracker), otherwise you may need
to save and restore GL state manually for that library to work.
-@section troubleshooting-debugging Debugging rendering
+@subsection troubleshooting-opengl-debugging Debugging rendering
- Use @ref GL::TimeQuery to find hot spots in the rendering code.
- @ref GL::DebugMessage::insert() "Mark relevant parts of code" to find them
diff --git a/doc/types.dox b/doc/types.dox
index 6831fec4b..6b055bb46 100644
--- a/doc/types.dox
+++ b/doc/types.dox
@@ -31,15 +31,19 @@ namespace Magnum {
@m_footernavigation
@m_keyword{Type system,,}
-The root @ref Magnum namespace defines a few aliases for essential math types.
-See its documentation for more information about usage.
+Magnum defines a variety of scalar, vector and matrix types. Most of the
+functionality is implemented using template classes in the @ref Math library,
+with the most common variants brought as typedefs into the root @ref Magnum
+namespace.
@section types-builtin Builtin types
-Magnum provides typedefs for builtin integral and floating-point arithmetic
-types to ensure portability (e.g. @ref Int is *always* 32bit), maintain
-consistency and reduce confusion (e.g. @ref std::int32_t, @cpp int @ce and
-@cpp GLint @ce all refer to the same type).
+Magnum provides its own typedefs for builtin integral and floating-point
+arithmetic types to ensure portability, maintain consistency and reduce
+confusion. E.g., the @ref Int typedef is guaranteed to *always* be 32-bit and
+Magnum's own code and documentation prefers to use it over a wild mixture of
+@ref std::int32_t, @cpp int @ce, @cpp GLint @ce and @cpp ALint @ce that all
+refer to the same type.
| Magnum type | Size | Equivalent GLSL type |
| ------------------ | -------------- | ----------------------- |
@@ -56,8 +60,8 @@ consistency and reduce confusion (e.g. @ref std::int32_t, @cpp int @ce and
| @ref Double | 64bit | @glsl double @ce |
Types not meant to be used in arithmetic (such as @cpp bool @ce or
-@cpp std::size_t @ce) or types which cannot be directly passed to GLSL shaders
-(such as @cpp long double @ce) have no typedefs.
+@cpp std::size_t @ce) or types which have no use in GPU computations (such as
+@cpp long double @ce) have no typedefs.
Types from the above table are then used to define other types. All following
types are aliases of corresponding types in @ref Math namespace. No suffix
@@ -121,11 +125,11 @@ about the differences.
@section types-binary Binary representation
-Scalar types with GLSL equivalent are verified to be exactly the same as
-corresponding `GL*` types. Matrix and vector classes have the same binary
-representation as corresponding array of numeric values without any additional
-data or padding (e.g. @cpp sizeof(Vector3i) == sizeof(Int[3]) @ce), all
-matrices are stored in column-major order.
+Scalar types with a GLSL equivalent are guaranteed to have exactly the same
+binary representation. Consequently, matrix and vector classes also have the
+same binary representation as corresponding array of numeric values without any
+additional data or padding (e.g. @cpp sizeof(Vector3i) == sizeof(Int[3]) @ce),
+all matrices are stored in column-major order.
This means that all scalar, matrix and vector types can be used directly for
filling GPU buffers and textures without any need for data extraction or
diff --git a/src/Magnum/GL/Context.h b/src/Magnum/GL/Context.h
index 94d7a4337..fe01327be 100644
--- a/src/Magnum/GL/Context.h
+++ b/src/Magnum/GL/Context.h
@@ -204,12 +204,12 @@ instances for other OpenGL contexts, *first* you need to "unset" the current one
with @ref makeCurrent() and *then* create another instance, which will then
become implicitly active:
-@snippet MagnumGL-framebuffer.cpp Context-makeCurrent-nullptr
+@snippet MagnumGL-application.cpp Context-makeCurrent-nullptr
Once all needed instances are created, switch between them right after making
the underlying GL context current:
-@snippet MagnumGL-framebuffer.cpp Context-makeCurrent
+@snippet MagnumGL-application.cpp Context-makeCurrent
@section GL-Context-multithreading Thread safety
diff --git a/src/Magnum/GL/DefaultFramebuffer.h b/src/Magnum/GL/DefaultFramebuffer.h
index 3f01d0ff7..27390c828 100644
--- a/src/Magnum/GL/DefaultFramebuffer.h
+++ b/src/Magnum/GL/DefaultFramebuffer.h
@@ -49,7 +49,7 @@ classes, pass the new size in your
@ref Platform::Sdl2Application::viewportEvent() "viewportEvent()"
implementation, for example:
-@snippet MagnumGL-framebuffer.cpp DefaultFramebuffer-usage-viewport
+@snippet MagnumGL-application.cpp DefaultFramebuffer-usage-viewport
Next thing you probably want is to clear all used buffers before performing
any drawing. Again, in case you're using one of the
@@ -57,7 +57,7 @@ any drawing. Again, in case you're using one of the
@ref Platform::Sdl2Application::drawEvent() "drawEvent()" implementation, for
example:
-@snippet MagnumGL-framebuffer.cpp DefaultFramebuffer-usage-clear
+@snippet MagnumGL-application.cpp DefaultFramebuffer-usage-clear
See documentation of particular functions and @ref Framebuffer documentation
for more involved usage, usage of non-default or multiple framebuffers.
diff --git a/src/Magnum/Magnum.h b/src/Magnum/Magnum.h
index 82fc79e15..12d7688cc 100644
--- a/src/Magnum/Magnum.h
+++ b/src/Magnum/Magnum.h
@@ -967,6 +967,36 @@ typedef Math::Matrix4x3 Matrix4x3h;
*/
typedef Math::Matrix4x4 Matrix4x4h;
+/**
+@brief Angle in half-float degrees
+@m_since_latest
+*/
+typedef Math::Deg Degh;
+
+/**
+@brief Angle in half-float radians
+@m_since_latest
+*/
+typedef Math::Rad Radh;
+
+/**
+@brief One-dimensional half-float range
+@m_since_latest
+*/
+typedef Math::Range1D Range1Dh;
+
+/**
+@brief Two-dimensional half-float range
+@m_since_latest
+*/
+typedef Math::Range2D Range2Dh;
+
+/**
+@brief Three-dimensional half-float range
+@m_since_latest
+*/
+typedef Math::Range3D Range3Dh;
+
/* Since 1.8.17, the original short-hand group closing doesn't work anymore.
FFS. */
/**
diff --git a/src/Magnum/Math/Angle.h b/src/Magnum/Math/Angle.h
index 9edb499c7..13f4e70d8 100644
--- a/src/Magnum/Math/Angle.h
+++ b/src/Magnum/Math/Angle.h
@@ -94,7 +94,7 @@ These silent errors are easily avoided by requiring explicit conversions:
@snippet MagnumMath.cpp Deg-usage-explicit-conversion
-@see @ref Magnum::Deg, @ref Magnum::Degd
+@see @ref Magnum::Deg, @ref Magnum::Degh, @ref Magnum::Degd
*/
template class Deg: public Unit {
public:
@@ -166,7 +166,7 @@ constexpr Deg operator "" _degf(long double value) { return Deg(Fl
@brief Angle in radians
See @ref Deg for more information.
-@see @ref Magnum::Rad, @ref Magnum::Radd
+@see @ref Magnum::Rad, @ref Magnum::Radh, @ref Magnum::Radd
*/
template class Rad: public Unit {
public:
diff --git a/src/Magnum/Math/CubicHermite.h b/src/Magnum/Math/CubicHermite.h
index bb7106d07..9e74148f6 100644
--- a/src/Magnum/Math/CubicHermite.h
+++ b/src/Magnum/Math/CubicHermite.h
@@ -137,7 +137,7 @@ template class CubicHermite {
constexpr /*implicit*/ CubicHermite(const T& inTangent, const T& point, const T& outTangent) noexcept: _inTangent{inTangent}, _point{point}, _outTangent{outTangent} {}
/**
- * @brief Construct subic Hermite spline point from another of different type
+ * @brief Construct cubic Hermite spline point from another of different type
*
* Performs only default casting on the values, no rounding or
* anything else.
diff --git a/src/Magnum/Math/Range.h b/src/Magnum/Math/Range.h
index 10e2333e6..91c7d21b9 100644
--- a/src/Magnum/Math/Range.h
+++ b/src/Magnum/Math/Range.h
@@ -348,8 +348,8 @@ template class Range {
Convenience alternative to @cpp Range<1, T> @ce. See @ref Range for more
information.
-@see @ref Range2D, @ref Range3D, @ref Magnum::Range1D, @ref Magnum::Range1Di,
- @ref Magnum::Range1Dd
+@see @ref Range2D, @ref Range3D, @ref Magnum::Range1D, @ref Magnum::Range1Dh,
+ @ref Magnum::Range1Dd, @ref Magnum::Range1Di
*/
#ifndef CORRADE_MSVC2015_COMPATIBILITY /* Multiple definitions still broken */
template using Range1D = Range<1, T>;
@@ -359,8 +359,8 @@ template using Range1D = Range<1, T>;
@brief Two-dimensional range
See @ref Range for more information.
-@see @ref Range1D, @ref Range3D, @ref Magnum::Range2D, @ref Magnum::Range2Di,
- @ref Magnum::Range2Dd
+@see @ref Range1D, @ref Range3D, @ref Magnum::Range2D, @ref Magnum::Range2Dh,
+ @ref Magnum::Range2Dd, @ref Magnum::Range2Di
*/
template class Range2D: public Range<2, T> {
public:
@@ -513,8 +513,8 @@ template class Range2D: public Range<2, T> {
@brief Three-dimensional range
See @ref Range for more information.
-@see @ref Range1D, @ref Range2D, @ref Magnum::Range3D, @ref Magnum::Range3Di,
- @ref Magnum::Range3Dd
+@see @ref Range1D, @ref Range2D, @ref Magnum::Range3D, @ref Magnum::Range3Dh,
+ @ref Magnum::Range3Dd, @ref Magnum::Range3Di
*/
template class Range3D: public Range<3, T> {
public:
diff --git a/src/Magnum/MeshTools/sceneconverter.cpp b/src/Magnum/MeshTools/sceneconverter.cpp
index e7c814b0c..7a04a5acd 100644
--- a/src/Magnum/MeshTools/sceneconverter.cpp
+++ b/src/Magnum/MeshTools/sceneconverter.cpp
@@ -45,6 +45,7 @@
#include "Magnum/Trade/MaterialData.h"
#include "Magnum/Trade/MeshData.h"
#include "Magnum/Trade/MeshObjectData3D.h"
+#include "Magnum/Trade/SkinData.h"
#include "Magnum/Trade/TextureData.h"
#include "Magnum/Trade/AbstractSceneConverter.h"
#include "Magnum/Trade/Implementation/converterUtilities.h"
@@ -289,6 +290,13 @@ used.)")
std::string name;
};
+ struct SkinInfo {
+ UnsignedInt skin;
+ UnsignedInt references;
+ Trade::SkinData3D data{{}, {}};
+ std::string name;
+ };
+
struct LightInfo {
UnsignedInt light;
UnsignedInt references;
@@ -338,6 +346,7 @@ used.)")
Containers::Array materialReferenceCount{importer->materialCount()};
Containers::Array lightReferenceCount{importer->lightCount()};
Containers::Array meshReferenceCount{importer->meshCount()};
+ Containers::Array skinReferenceCount{importer->skin3DCount()};
for(UnsignedInt i = 0; i != importer->object3DCount(); ++i) {
Containers::Pointer object = importer->object3D(i);
if(!object) continue;
@@ -347,6 +356,8 @@ used.)")
++meshReferenceCount[meshObject.instance()];
if(std::size_t(meshObject.material()) < materialReferenceCount.size())
++materialReferenceCount[meshObject.material()];
+ if(std::size_t(meshObject.skin()) < skinReferenceCount.size())
+ ++skinReferenceCount[meshObject.skin()];
} else if(object->instanceType() == Trade::ObjectInstanceType3D::Light) {
if(std::size_t(object->instance()) < lightReferenceCount.size())
++lightReferenceCount[object->instance()];
@@ -374,6 +385,27 @@ used.)")
arrayAppend(animationInfos, std::move(info));
}
+ /* Skin properties */
+ Containers::Array skinInfos;
+ for(UnsignedInt i = 0; i != importer->skin3DCount(); ++i) {
+ Containers::Optional skin;
+ {
+ Duration d{importTime};
+ if(!(skin = importer->skin3D(i))) {
+ error = true;
+ continue;
+ }
+ }
+
+ SkinInfo info{};
+ info.skin = i;
+ info.name = importer->skin3DName(i);
+ info.references = skinReferenceCount[i];
+ info.data = *std::move(skin);
+
+ arrayAppend(skinInfos, std::move(info));
+ }
+
/* Light properties */
Containers::Array lightInfos;
for(UnsignedInt i = 0; i != importer->lightCount(); ++i) {
@@ -569,6 +601,19 @@ used.)")
not so much for things like complex numbers or quats */
}
}
+ for(const SkinInfo& info: skinInfos) {
+ Debug d;
+ d << "Skin" << info.skin;
+ /* Print reference count only if there actually is a scene,
+ otherwise this information is useless */
+ if(importer->object3DCount())
+ d << Utility::formatString("(referenced by {} objects)", info.references);
+ d << Debug::nospace << ":";
+ if(!info.name.empty()) d << info.name;
+
+ d << Debug::newline << " Joints:" << info.data.joints();
+ }
+
for(const LightInfo& info: lightInfos) {
Debug d;
d << "Light" << info.light;
diff --git a/src/Magnum/Trade/MeshData.cpp b/src/Magnum/Trade/MeshData.cpp
index 080a74988..e4df296c6 100644
--- a/src/Magnum/Trade/MeshData.cpp
+++ b/src/Magnum/Trade/MeshData.cpp
@@ -202,10 +202,10 @@ MeshData::MeshData(const MeshPrimitive primitive, const DataFlags indexDataFlags
MeshData::MeshData(const MeshPrimitive primitive, const UnsignedInt vertexCount, const void* const importerState) noexcept: MeshData{primitive, {}, MeshIndexData{}, {}, {}, vertexCount, importerState} {}
-MeshData::~MeshData() = default;
-
MeshData::MeshData(MeshData&&) noexcept = default;
+MeshData::~MeshData() = default;
+
MeshData& MeshData::operator=(MeshData&&) noexcept = default;
Containers::ArrayView MeshData::mutableIndexData() & {
diff --git a/src/Magnum/Trade/MeshData.h b/src/Magnum/Trade/MeshData.h
index 6b84034ae..0a2912549 100644
--- a/src/Magnum/Trade/MeshData.h
+++ b/src/Magnum/Trade/MeshData.h
@@ -150,7 +150,7 @@ enum class MeshAttribute: UnsignedShort {
/**
* This and all higher values are for importer-specific attributes. Can be
* of any type. See documentation of a particular importer for details.
- * @see @ref isMeshAttributeCustom(MeshAttribute)
+ * @see @ref isMeshAttributeCustom(),
* @ref meshAttributeCustom(MeshAttribute),
* @ref meshAttributeCustom(UnsignedShort)
*/
@@ -294,22 +294,23 @@ or @ref MeshTools::interleave(const Trade::MeshData& data, Containers::ArrayView
@section Trade-MeshAttributeData-usage Usage
-The most straightforward usage is constructing an instance from a pair of
+The most straightforward usage is constructing an instance from a pair of a
@ref MeshAttribute and a strided view. The @ref VertexFormat gets inferred from
the view type:
@snippet MagnumTrade.cpp MeshAttributeData-usage
-Alternatively, you can pass a typeless @cpp const void @ce view and supply
-@ref VertexFormat explicitly, or a 2D view.
+Alternatively, you can pass a typeless @cpp const void @ce or a 2D view and
+supply @ref VertexFormat explicitly.
@subsection Trade-MeshAttributeData-usage-offset-only Offset-only attribute data
If the actual attribute data location is not known yet, the instance can be
created as "offset-only", meaning the actual view gets created only later when
passed to a @ref MeshData instance with a concrete vertex data array. This is
-useful for example when vertex layout is static (and thus can be defined at
-compile time), but the actual data is allocated / populated at runtime:
+useful mainly to avoid pointer patching during data serialization, but also for
+example when vertex layout is static (and thus can be defined at compile time),
+but the actual data is allocated / populated at runtime:
@snippet MagnumTrade.cpp MeshAttributeData-usage-offset-only
@@ -350,8 +351,8 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
* attributes.
*
* Expects that @p data stride is large enough to fit all @p arraySize
- * items of @p type, @p type corresponds to @p name and @p arraySize is
- * zero for builtin attributes.
+ * items of @p format, @p format corresponds to @p name and
+ * @p arraySize is zero for builtin attributes.
*/
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView1D& data, UnsignedShort arraySize = 0) noexcept;
@@ -364,8 +365,8 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
* attributes.
*
* Expects that the second dimension of @p data is contiguous and its
- * size matches @p type and @p arraSize, that @p type corresponds to
- * @p name and @p arraySize is zero for builtin attributes.
+ * size matches @p format and @p arraySize, that @p format corresponds
+ * to @p name and @p arraySize is zero for builtin attributes.
*/
explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView2D& data, UnsignedShort arraySize = 0) noexcept;
@@ -515,8 +516,7 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
* @brief Type-erased attribute data
*
* Expects that the attribute is not offset-only, in that case use the
- * @ref data(Containers::ArrayView) const overload
- * instead.
+ * @ref data(Containers::ArrayView) const overload instead.
* @see @ref isOffsetOnly()
*/
constexpr Containers::StridedArrayView1D data() const {
@@ -524,7 +524,7 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
/* We're *sure* the view is correct, so faking the view size */
/** @todo better ideas for the StridedArrayView API? */
{_data.pointer, ~std::size_t{}}, _vertexCount,
- (CORRADE_CONSTEXPR_ASSERT(!_isOffsetOnly, "Trade::MeshAttributeData::data(): the attribute is a relative offset, supply a data array"), _stride)};
+ (CORRADE_CONSTEXPR_ASSERT(!_isOffsetOnly, "Trade::MeshAttributeData::data(): the attribute is offset-only, supply a data array"), _stride)};
}
/**
@@ -544,7 +544,9 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
private:
friend MeshData;
- /* nullptr first, to avoid accidental matches as much as possible */
+ /* Delegated to by all ArrayView constructors, which additionally check
+ either stride or second dimension size. Nullptr first, to avoid
+ accidental matches as much as possible. */
constexpr explicit MeshAttributeData(std::nullptr_t, MeshAttribute name, VertexFormat format, const Containers::StridedArrayView1D& data, UnsignedShort arraySize) noexcept;
VertexFormat _format;
@@ -644,10 +646,15 @@ cases when it's desirable to modify the data in-place, there's the
@ref mutableIndexData(), @ref mutableVertexData(), @ref mutableIndices() and
@ref mutableAttribute() set of functions. To use these, you need to check that
the data are mutable using @ref indexDataFlags() or @ref vertexDataFlags()
-first. The following snippet applies a transformation to the mesh data:
+first, and if not then you may want to make a mutable copy first using
+@ref MeshTools::owned(). The following snippet applies a transformation to the
+mesh positions:
@snippet MagnumTrade.cpp MeshData-usage-mutable
+If the transformation includes a rotation or non-uniform scaling, you may want
+to do a similar operation with normals and tangents as well.
+
@section Trade-MeshData-populating Populating an instance
A @ref MeshData instance by default takes over the ownership of an
@@ -938,14 +945,14 @@ class MAGNUM_TRADE_EXPORT MeshData {
*/
explicit MeshData(MeshPrimitive primitive, UnsignedInt vertexCount, const void* importerState = nullptr) noexcept;
- ~MeshData();
-
/** @brief Copying is not allowed */
MeshData(const MeshData&) = delete;
/** @brief Move constructor */
MeshData(MeshData&&) noexcept;
+ ~MeshData();
+
/** @brief Copying is not allowed */
MeshData& operator=(const MeshData&) = delete;
@@ -1301,11 +1308,13 @@ class MAGNUM_TRADE_EXPORT MeshData {
*
* The @p id is expected to be smaller than @ref attributeCount() const.
* The second dimension represents the actual data type (its size is
- * equal to format size for known @ref VertexFormat values and to
- * attribute stride for implementation-specific values) and is
- * guaranteed to be contiguous. Use the templated overload below to get
- * the attribute in a concrete type.
+ * equal to format size for known @ref VertexFormat values, possibly
+ * multiplied by array size, and to attribute stride for
+ * implementation-specific values) and is guaranteed to be contiguous.
+ * Use the templated overload below to get the attribute in a concrete
+ * type.
* @see @ref Corrade::Containers::StridedArrayView::isContiguous(),
+ * @ref vertexFormatSize(),
* @ref isVertexFormatImplementationSpecific()
*/
Containers::StridedArrayView2D attribute(UnsignedInt id) const;
@@ -1338,7 +1347,7 @@ class MAGNUM_TRADE_EXPORT MeshData {
* to usual types, but note that these operations involve extra
* allocation and data conversion.
* @see @ref attribute(MeshAttribute, UnsignedInt) const,
- * @ref mutableAttribute(MeshAttribute, UnsignedInt),
+ * @ref mutableAttribute(UnsignedInt),
* @ref isVertexFormatImplementationSpecific(),
* @ref attributeArraySize()
*/
@@ -1409,13 +1418,15 @@ class MAGNUM_TRADE_EXPORT MeshData {
* correspond to @ref attributeFormat(MeshAttribute, UnsignedInt) const.
* Expects that the vertex format is *not* implementation-specific, in
* that case you can only access the attribute via the typeless
- * @ref attribute(MeshAttribute, UnsignedInt) const above. You can also
- * use the non-templated @ref positions2DAsArray(),
- * @ref positions3DAsArray(), @ref normalsAsArray(),
- * @ref textureCoordinates2DAsArray() and @ref colorsAsArray()
- * accessors to get common attributes converted to usual types, but
- * note that these operations involve extra data conversion and an
- * allocation.
+ * @ref attribute(MeshAttribute, UnsignedInt) const above. The
+ * attribute is also expected to not be an array, in that case you need
+ * to use the overload below by using @cpp T[] @ce instead of
+ * @cpp T @ce. You can also use the non-templated
+ * @ref positions2DAsArray(), @ref positions3DAsArray(),
+ * @ref normalsAsArray(), @ref textureCoordinates2DAsArray() and
+ * @ref colorsAsArray() accessors to get common attributes converted to
+ * usual types, but note that these operations involve extra data
+ * conversion and an allocation.
* @see @ref attribute(UnsignedInt) const,
* @ref mutableAttribute(MeshAttribute, UnsignedInt),
* @ref isVertexFormatImplementationSpecific()
@@ -1808,7 +1819,7 @@ namespace Implementation {
/* Implicit mapping from a format to enum (1:1) */
template constexpr VertexFormat vertexFormatFor() {
/* C++ why there isn't an obvious way to do such a thing?! */
- static_assert(sizeof(T) == 0, "unsupported attribute type");
+ static_assert(sizeof(T) == 0, "unsupported vertex format");
return {};
}
#ifndef DOXYGEN_GENERATING_OUTPUT
@@ -2093,7 +2104,14 @@ constexpr MeshAttributeData::MeshAttributeData(const MeshAttribute name, const V
template constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView1D& data) noexcept: MeshAttributeData{nullptr, name, Implementation::vertexFormatFor::type>(), data, 0} {}
-template constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView2D& data) noexcept: MeshAttributeData{(CORRADE_CONSTEXPR_ASSERT(data.stride()[1] == sizeof(T), "Trade::MeshAttributeData: second view dimension is not contiguous"), nullptr), name, Implementation::vertexFormatFor::type>(), Containers::StridedArrayView1D{{data.data(), ~std::size_t{}}, data.size()[0], data.stride()[0]}, UnsignedShort(data.size()[1])} {}
+template constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView2D& data) noexcept: MeshAttributeData{
+ /* Not using isContiguous<1>() as that's not constexpr */
+ (CORRADE_CONSTEXPR_ASSERT(data.stride()[1] == sizeof(T), "Trade::MeshAttributeData: second view dimension is not contiguous"), nullptr),
+ name,
+ Implementation::vertexFormatFor::type>(),
+ Containers::StridedArrayView1D{{data.data(), ~std::size_t{}}, data.size()[0], data.stride()[0]},
+ UnsignedShort(data.size()[1])
+} {}
template Containers::ArrayView MeshData::indices() const {
CORRADE_ASSERT(isIndexed(),
diff --git a/src/Magnum/Trade/Test/MeshDataTest.cpp b/src/Magnum/Trade/Test/MeshDataTest.cpp
index ce9bbfa0f..1169f45c7 100644
--- a/src/Magnum/Trade/Test/MeshDataTest.cpp
+++ b/src/Magnum/Trade/Test/MeshDataTest.cpp
@@ -576,8 +576,11 @@ void MeshDataTest::constructAttribute() {
CORRADE_COMPARE(positions.offset(positionData), 0);
CORRADE_COMPARE(positions.stride(), sizeof(Vector2));
CORRADE_VERIFY(positions.data().data() == positionData);
- /* This is allowed too for simplicity, it just ignores the parameter */
- CORRADE_VERIFY(positions.data(positionData).data() == positionData);
+
+ /* This is allowed too for simplicity, the parameter has to be large enough
+ tho */
+ char someArray[3*sizeof(Vector2)];
+ CORRADE_VERIFY(positions.data(someArray).data() == positionData);
constexpr MeshAttributeData cpositions{MeshAttribute::Position, Containers::arrayView(Positions)};
constexpr bool isOffsetOnly = cpositions.isOffsetOnly();
@@ -605,6 +608,9 @@ void MeshDataTest::constructAttributeDefault() {
}
void MeshDataTest::constructAttributeCustom() {
+ /* Verifying it doesn't hit any assertion about disallowed type for given
+ attribute */
+
const Short idData[3]{};
MeshAttributeData ids{meshAttributeCustom(13), Containers::arrayView(idData)};
CORRADE_COMPARE(ids.name(), meshAttributeCustom(13));
@@ -789,11 +795,13 @@ void MeshDataTest::constructAttributeWrongDataAccess() {
CORRADE_VERIFY(!a.isOffsetOnly());
CORRADE_VERIFY(b.isOffsetOnly());
+ a.data(positionData); /* This is fine, no asserts */
+
std::ostringstream out;
Error redirectError{&out};
b.data();
CORRADE_COMPARE(out.str(),
- "Trade::MeshAttributeData::data(): the attribute is a relative offset, supply a data array\n");
+ "Trade::MeshAttributeData::data(): the attribute is offset-only, supply a data array\n");
}
constexpr Vector2 ArrayVertexData[3*4]
@@ -886,7 +894,7 @@ void MeshDataTest::constructArrayAttribute2DNonContiguous() {
void MeshDataTest::constructArrayAttributeTypeErased() {
Vector2 vertexData[3*4];
Containers::StridedArrayView1D attribute{vertexData, 3, 4*sizeof(Vector2)};
- MeshAttributeData data{meshAttributeCustom(35), VertexFormat::Vector2, attribute, 4};
+ MeshAttributeData data{meshAttributeCustom(35), VertexFormat::Vector2, Containers::arrayCast(attribute), 4};
CORRADE_VERIFY(!data.isOffsetOnly());
CORRADE_COMPARE(data.name(), meshAttributeCustom(35));
CORRADE_COMPARE(data.format(), VertexFormat::Vector2);