From 491b724a8615f693f8c3c33650176e606492ed11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 23 Nov 2021 21:28:41 +0100 Subject: [PATCH] GL: refresh Mesh and Buffer docs to be less shit. Looking at the snippets, these seem to have been written back when there was no builtin shaders yet, it seems, not to mention MeshTools::compile(), Trade::MeshData or any of the other high-level APIs. Rather overwhelming to just throw huge code snippets at the user, explaining a workflow with a custom-made mesh that's going to be drawn with a custom-made shader, which is like level 999 of using the GL library. --- doc/snippets/MagnumGL.cpp | 258 +++++++++++++++++------------------ doc/snippets/MagnumTrade.cpp | 1 + src/Magnum/GL/Buffer.h | 45 +++--- src/Magnum/GL/Mesh.h | 170 +++++++++++++++++------ src/Magnum/Trade/MeshData.h | 6 + 5 files changed, 285 insertions(+), 195 deletions(-) diff --git a/doc/snippets/MagnumGL.cpp b/doc/snippets/MagnumGL.cpp index dd463a392..0ba1752cb 100644 --- a/doc/snippets/MagnumGL.cpp +++ b/doc/snippets/MagnumGL.cpp @@ -47,11 +47,14 @@ #include "Magnum/GL/Texture.h" #include "Magnum/GL/TextureFormat.h" #include "Magnum/GL/Version.h" +#include "Magnum/Math/Color.h" +#include "Magnum/Math/Half.h" #include "Magnum/Math/Matrix4.h" #include "Magnum/MeshTools/Interleave.h" #include "Magnum/MeshTools/CompressIndices.h" #include "Magnum/Primitives/Cube.h" #include "Magnum/Primitives/Plane.h" +#include "Magnum/Shaders/FlatGL.h" #include "Magnum/Shaders/PhongGL.h" #include "Magnum/Trade/MeshData.h" @@ -82,6 +85,8 @@ #include "Magnum/GL/RectangleTexture.h" #endif +#define DOXYGEN_IGNORE(...) __VA_ARGS__ + using namespace Magnum; using namespace Magnum::Math::Literals; @@ -446,9 +451,12 @@ GL::BufferImage2D image = framebuffer.read(framebuffer.viewport(), #endif { -GL::Buffer buffer; /* [Buffer-setdata] */ -Containers::ArrayView data; +const Vector3 data[]{ + DOXYGEN_IGNORE({}) +}; + +GL::Buffer buffer; buffer.setData(data); GL::Buffer buffer2{data}; // or construct & fill in a single step @@ -458,10 +466,23 @@ GL::Buffer buffer2{data}; // or construct & fill in a single step { GL::Buffer buffer; /* [Buffer-setdata-stl] */ -std::vector data; +std::vector data = DOXYGEN_IGNORE({}); buffer.setData(data); /* [Buffer-setdata-stl] */ +} + +#ifndef MAGNUM_TARGET_GLES +{ +Containers::ArrayView data; +/* [Buffer-setstorage] */ +GL::Buffer buffer; +buffer.setStorage(data, {}); +/* [Buffer-setstorage] */ +} +#endif +{ +GL::Buffer buffer; /* [Buffer-setdata-allocate] */ buffer.setData({nullptr, 200*sizeof(Vector3)}); /* [Buffer-setdata-allocate] */ @@ -471,8 +492,9 @@ buffer.setData({nullptr, 200*sizeof(Vector3)}); { GL::Buffer buffer; /* [Buffer-map] */ -Containers::ArrayView data = Containers::arrayCast(buffer.map(0, - 200*sizeof(Vector3), GL::Buffer::MapFlag::Write|GL::Buffer::MapFlag::InvalidateBuffer)); +Containers::ArrayView data = Containers::arrayCast( + buffer.map(0, 200*sizeof(Vector3), + GL::Buffer::MapFlag::Write|GL::Buffer::MapFlag::InvalidateBuffer)); CORRADE_INTERNAL_ASSERT(data); for(Vector3& d: data) d = {/*...*/}; @@ -483,8 +505,9 @@ CORRADE_INTERNAL_ASSERT_OUTPUT(buffer.unmap()); { GL::Buffer buffer; /* [Buffer-flush] */ -Containers::ArrayView data = Containers::arrayCast(buffer.map(0, - 200*sizeof(Vector3), GL::Buffer::MapFlag::Write|GL::Buffer::MapFlag::FlushExplicit)); +Containers::ArrayView data = Containers::arrayCast( + buffer.map(0, 200*sizeof(Vector3), + GL::Buffer::MapFlag::Write|GL::Buffer::MapFlag::FlushExplicit)); CORRADE_INTERNAL_ASSERT(data); for(std::size_t i: {7, 27, 56, 128}) { data[i] = {/*...*/}; @@ -497,14 +520,15 @@ CORRADE_INTERNAL_ASSERT_OUTPUT(buffer.unmap()); { /* [Buffer-webgl-nope] */ -GL::Buffer vertices, indices; +GL::Buffer vertices; +GL::Buffer indices; /* [Buffer-webgl-nope] */ } { /* [Buffer-webgl] */ -GL::Buffer vertices{GL::Buffer::TargetHint::Array}, - indices{GL::Buffer::TargetHint::ElementArray}; +GL::Buffer vertices{GL::Buffer::TargetHint::Array}; +GL::Buffer indices{GL::Buffer::TargetHint::ElementArray}; /* [Buffer-webgl] */ } @@ -1016,156 +1040,112 @@ framebuffer.mapForDraw({ #endif { -/* [Mesh-nonindexed] */ -/* Custom shader, needing only position data */ -class MyShader: public GL::AbstractShaderProgram { - public: - typedef GL::Attribute<0, Vector3> Position; - - // ... -}; - -/* Fill vertex buffer with position data */ -Vector3 positions[30]{ - // ... +/* [Mesh-vertices] */ +const Vector3 positions[]{ + DOXYGEN_IGNORE({}) }; -GL::Buffer vertexBuffer; -vertexBuffer.setData(positions, GL::BufferUsage::StaticDraw); +GL::Buffer vertices{positions}; -/* Configure the mesh, add vertex buffer */ GL::Mesh mesh; -mesh.setPrimitive(MeshPrimitive::Triangles) - .addVertexBuffer(vertexBuffer, 0, MyShader::Position{}) - .setCount(30); -/* [Mesh-nonindexed] */ +mesh.setCount(Containers::arraySize(positions)) + .addVertexBuffer(vertices, 0, Shaders::FlatGL3D::Position{}); +/* [Mesh-vertices] */ } { -/* [Mesh-interleaved] */ -/* Non-indexed primitive with positions and normals */ -Trade::MeshData plane = Primitives::planeSolid(); - -/* Fill a vertex buffer with interleaved position and normal data */ -GL::Buffer buffer; -buffer.setData(MeshTools::interleave(plane.positions3DAsArray(), - plane.normalsAsArray())); +/* [Mesh-vertices-interleaved] */ +struct Vertex { + Vector3 position; + Vector3 normal; +}; +const Vertex vertexData[]{ + DOXYGEN_IGNORE({}) +}; +GL::Buffer vertices{vertexData}; -/* Configure the mesh, add the vertex buffer */ GL::Mesh mesh; -mesh.setPrimitive(plane.primitive()) - .setCount(plane.vertexCount()) - .addVertexBuffer(buffer, 0, Shaders::PhongGL::Position{}, Shaders::PhongGL::Normal{}); -/* [Mesh-interleaved] */ +mesh.setCount(Containers::arraySize(vertexData)) + .addVertexBuffer(vertices, 0, + Shaders::PhongGL::Position{}, + Shaders::PhongGL::Normal{}); +/* [Mesh-vertices-interleaved] */ } { -/* [Mesh-indexed] */ -/* Custom shader */ -class MyShader: public GL::AbstractShaderProgram { - public: - typedef GL::Attribute<0, Vector3> Position; - - // ... -}; - -/* Fill vertex buffer with position data */ -Vector3 positions[240]{ - // ... -}; -GL::Buffer vertexBuffer; -vertexBuffer.setData(positions); - -/* Fill index buffer with index data */ -UnsignedByte indices[75]{ - // ... +GL::Mesh mesh; +/* [Mesh-indices] */ +const UnsignedInt indexData[]{ + DOXYGEN_IGNORE(0) }; -GL::Buffer indexBuffer; -indexBuffer.setData(indices); +GL::Buffer indices{indexData}; -/* Configure the mesh, add both vertex and index buffer */ -GL::Mesh mesh; -mesh.setPrimitive(MeshPrimitive::Triangles) - .setCount(75) - .addVertexBuffer(vertexBuffer, 0, MyShader::Position{}) - .setIndexBuffer(indexBuffer, 0, GL::MeshIndexType::UnsignedByte, 176, 229); -/* [Mesh-indexed] */ +DOXYGEN_IGNORE() +mesh.setIndexBuffer(indices, 0, MeshIndexType::UnsignedInt); +/* [Mesh-indices] */ } { -/* [Mesh-indexed-tools] */ -// Indexed primitive -Trade::MeshData cube = Primitives::cubeSolid(); - -// Fill vertex buffer with interleaved position and normal data -GL::Buffer vertexBuffer; -vertexBuffer.setData(MeshTools::interleave(cube.positions3DAsArray(), - cube.normalsAsArray())); - -// Compress index data -Containers::Array indexData; -MeshIndexType indexType; -std::tie(indexData, indexType) = MeshTools::compressIndices(cube.indices()); - -// Fill index buffer -GL::Buffer indexBuffer; -indexBuffer.setData(indexData); - -// Configure the mesh, add both vertex and index buffer GL::Mesh mesh; -mesh.setPrimitive(cube.primitive()) - .setCount(cube.indexCount()) - .addVertexBuffer(vertexBuffer, 0, Shaders::PhongGL::Position{}, Shaders::PhongGL::Normal{}) - .setIndexBuffer(indexBuffer, 0, indexType); -/* [Mesh-indexed-tools] */ +/* [Mesh-vertices-interleaved-tool] */ +Containers::ArrayView positions = DOXYGEN_IGNORE({}); +Containers::ArrayView normals = DOXYGEN_IGNORE({}); +GL::Buffer vertices{MeshTools::interleave(positions, normals)}; + +DOXYGEN_IGNORE() +mesh.addVertexBuffer(vertices, 0, + Shaders::PhongGL::Position{}, + Shaders::PhongGL::Normal{}); +/* [Mesh-vertices-interleaved-tool] */ } -#ifndef MAGNUM_TARGET_GLES { -/* [Mesh-formats] */ -// Custom shader with colors specified as four floating-point values -class MyShader: public GL::AbstractShaderProgram { - public: - typedef GL::Attribute<0, Vector3> Position; - typedef GL::Attribute<1, Color4> Color; +GL::Mesh mesh; +const UnsignedInt indexData[1]{}; +/* [Mesh-indices-tool] */ +Containers::Array compressed; +MeshIndexType type; +std::tie(compressed, type) = MeshTools::compressIndices(indexData); +GL::Buffer indices{compressed}; - // ... -}; +DOXYGEN_IGNORE() +mesh.setIndexBuffer(indices, 0, type); +/* [Mesh-indices-tool] */ +} -/* Initial mesh configuration */ +{ GL::Mesh mesh; -mesh.setPrimitive(MeshPrimitive::Triangles) - .setCount(30); - -/* Fill position buffer with positions specified as two-component XY (i.e., - no Z component, which is meant to be always 0) */ -Vector2 positions[30]{ - // ... +/* [Mesh-formats] */ +struct Packed { + Vector3h position; + Short:16; + Vector3s normal; + Short:16; }; -GL::Buffer positionBuffer; -positionBuffer.setData(positions, GL::BufferUsage::StaticDraw); - -/* Specify layout of positions buffer -- only two components, unspecified Z - component will be automatically set to 0 */ -mesh.addVertexBuffer(positionBuffer, 0, - MyShader::Position{MyShader::Position::Components::Two}); - -/* Fill color buffer with colors specified as four-byte BGRA (i.e., directly - from a TGA file) */ -UnsignedByte colors[4*30]{ - // ... +const Packed vertexData[]{ + DOXYGEN_IGNORE({}) }; -GL::Buffer colorBuffer; -colorBuffer.setData(colors, GL::BufferUsage::StaticDraw); - -/* Specify color buffer layout -- BGRA, each component unsigned byte and we - want to normalize them from [0, 255] to [0.0f, 1.0f] */ -mesh.addVertexBuffer(colorBuffer, 0, MyShader::Color{ - MyShader::Color::Components::BGRA, - MyShader::Color::DataType::UnsignedByte, - MyShader::Color::DataOption::Normalized}); +GL::Buffer vertices{vertexData}; + +DOXYGEN_IGNORE() +mesh.addVertexBuffer(vertices, 0, + Shaders::PhongGL::Position{Shaders::PhongGL::Position::Components::Three, + Shaders::PhongGL::Position::DataType::Half}, + 2, + Shaders::PhongGL::Normal{Shaders::PhongGL::Normal::Components::Three, + Shaders::PhongGL::Normal::DataType::Short, + Shaders::PhongGL::Normal::DataOption::Normalized}, + 2); /* [Mesh-formats] */ + +/* [Mesh-formats-vertexformat] */ +mesh.addVertexBuffer(vertices, offsetof(Packed, position), sizeof(Packed), + GL::DynamicAttribute{Shaders::PhongGL::Position{}, + VertexFormat::Vector3h}); +mesh.addVertexBuffer(vertices, offsetof(Packed, normal), sizeof(Packed), + GL::DynamicAttribute{Shaders::PhongGL::Normal{}, + VertexFormat::Vector3sNormalized}); +/* [Mesh-formats-vertexformat] */ } -#endif { GL::Mesh mesh; @@ -1182,7 +1162,7 @@ mesh.addVertexBuffer(colorBuffer, 0, 4, GL::DynamicAttribute{ GL::Mesh mesh; /* [Mesh-buffer-ownership] */ GL::Buffer vertices, indices; -// ... +DOXYGEN_IGNORE() mesh.addVertexBuffer(std::move(vertices), 0, Shaders::PhongGL::Position{}, Shaders::PhongGL::Normal{}) @@ -1195,6 +1175,16 @@ mesh.addVertexBuffer(vertices, 0, Shaders::PhongGL::Position{}, 20) /* [Mesh-buffer-ownership-multiple] */ } +{ +GL::Mesh mesh; +/* [Mesh-draw] */ +Shaders::PhongGL shader{DOXYGEN_IGNORE()}; +DOXYGEN_IGNORE() +shader.draw(mesh); +/* [Mesh-draw] */ +} + + { /* [Mesh-addVertexBuffer1] */ GL::Buffer buffer; diff --git a/doc/snippets/MagnumTrade.cpp b/doc/snippets/MagnumTrade.cpp index fa9c5aad0..573c44c5a 100644 --- a/doc/snippets/MagnumTrade.cpp +++ b/doc/snippets/MagnumTrade.cpp @@ -657,6 +657,7 @@ Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, #ifdef MAGNUM_TARGET_GL { +/* This snippet is also used by GL::Mesh, bear that in mind when updating */ /* [MeshData-usage-compile] */ Trade::MeshData data = DOXYGEN_IGNORE(Trade::MeshData{MeshPrimitive::Points, 0}); diff --git a/src/Magnum/GL/Buffer.h b/src/Magnum/GL/Buffer.h index 1e9baaba4..102f0afb7 100644 --- a/src/Magnum/GL/Buffer.h +++ b/src/Magnum/GL/Buffer.h @@ -135,23 +135,36 @@ namespace Implementation { struct BufferState; } /** @brief Buffer -Encapsulates one OpenGL buffer object and provides functions for convenient -data updates. +Wraps an OpenGL buffer object. -@section GL-Buffer-data-updating Data updating +@section GL-Buffer-data-upload Data upload -Default way to set or update buffer data with @ref setData(), @ref setSubData() -or the shorthand @ref Buffer() constructor is to use -@ref Corrade::Containers::ArrayView. See its documentation for more information -about automatic conversions etc. +Data can be uploaded to the buffer with @ref setData(), @ref setSubData() or +the shorthand @ref Buffer(Containers::ArrayView, BufferUsage) "Buffer()" +constructor. All these functions accept a @relativeref{Corrade,Containers::ArrayView}, which is among other things +implicitly convertible from statically sized C arrays or can be constructed +from a pair of a pointer and a size. You can optionally specify a usage hint +in the second argument, which defaults to @ref BufferUsage::StaticDraw. @snippet MagnumGL.cpp Buffer-setdata -If you @cpp #include @ce @ref Corrade/Containers/ArrayViewStl.h, you can also -pass directly STL types such as @ref std::vector or @link std::array @endlink: +Furthermore, if you @cpp #include @ce @ref Corrade/Containers/ArrayViewStl.h, +you can also directly pass STL types such as a @ref std::vector or a +@ref std::array; with @ref Corrade/Containers/ArrayViewStlSpan.h the +@ref std::span is convertible to it as well: @snippet MagnumGL.cpp Buffer-setdata-stl +An alternative to @ref setData() that provides more flexibility and better +performance guarantees is @ref setStorage(). It's similar in spirit to texture +storage, however as it's only available on desktop OpenGL 4.4 +(@gl_extension{ARB,buffer_storage}), its usage isn't encouraged like with +textures. The minimal variant of the call shown below creates an immutable +buffer from given data in device memory, in the second argument you can specify +@ref StorageFlags that make it CPU-accessible, (persistently) mappable etc. + +@snippet MagnumGL.cpp Buffer-setstorage + @section GL-Buffer-data-mapping Memory mapping Buffer data can be also updated asynchronously. First you need to allocate @@ -178,19 +191,19 @@ Buffers in @ref MAGNUM_TARGET_WEBGL "WebGL" need to be bound only to one unique target, i.e., @ref Buffer bound to @ref Buffer::TargetHint::Array cannot be later rebound to @ref Buffer::TargetHint::ElementArray. However, Magnum by default uses any sufficient target when binding the buffer internally (e.g. for -setting data). To avoid GL errors, the following, while completely fine on -desktop, is not sufficient on WebGL: +setting data). Which means the following, while completely fine on desktop and +OpenGL ES, is not sufficient on WebGL: @snippet MagnumGL.cpp Buffer-webgl-nope -You have to set target hint to desired target, either in constructor or using -@ref Buffer::setTargetHint() like this (and similarly for other bufffer types -such as UBOs): +To avoid GL errors, you have to set target hint to desired target, either in +the constructor or using @ref setTargetHint(). A similar care needs to be taken +for uniform buffers and other types of buffers. @snippet MagnumGL.cpp Buffer-webgl -To simplify debugging, @ref Mesh checks proper target hint when adding vertex -and index buffers in WebGL. +To simplify debugging, the @ref Mesh class checks proper target hint when +adding vertex and index buffers under WebGL. @section GL-Buffer-performance-optimizations Performance optimizations diff --git a/src/Magnum/GL/Mesh.h b/src/Magnum/GL/Mesh.h index d5082554c..37a398ba6 100644 --- a/src/Magnum/GL/Mesh.h +++ b/src/Magnum/GL/Mesh.h @@ -213,81 +213,155 @@ template struct IsDynamicAttribute: std::true_type /** @brief Mesh +Wraps an OpenGL vertex array object, or a collection of buffers and attribute +bindings in case vertex array objects are not available or are disabled. + +@section GL-Mesh-configuration-compile Quick usage with MeshTools::compile() + +If you have a @ref Trade::MeshData instance that you got for example from +@ref Trade::AbstractImporter::mesh() or from the @ref Primitives library, the +simplest possible way is to use @ref MeshTools::compile(): + +@snippet MagnumTrade.cpp MeshData-usage-compile + +This one-liner uploads the data and configures the mesh for all attributes +known by Magnum that are present in it, making it suitable to be drawn by +builtin shaders. It's however rather opaque and the @ref Trade::MeshData may be +an overly generic abstraction if you already have your vertex data in known +types. Continue below to see how to configure a mesh for builtin shaders with +lower-level APIs. + +@m_class{m-note m-success} + +@par + A generic mesh setup using the high-level utility is used in the + @ref examples-primitives and @ref examples-viewer examples. + @section GL-Mesh-configuration Mesh configuration -You have to specify at least primitive and vertex/index count using -@ref setPrimitive() and @ref setCount(). Then fill your vertex buffers with -data, add them to the mesh and specify @ref Attribute "shader attribute" layout -inside the buffers using @ref addVertexBuffer(). You can also use -@ref MeshTools::interleave() to conveniently interleave vertex data. +A mesh is, at the very least, a @ref MeshPrimitive and associated vertex/index +count. To prevent accidentally drawing empty meshes, you're required to call +@ref setCount() always, the primitive is however implicitly +@ref MeshPrimitive::Triangles and you can change it either in the constructor +or via @ref setPrimitive(). If @ref setCount() (or @ref setInstanceCount()) is +zero, the mesh is considered empty and no draw commands are issued when calling +@ref AbstractShaderProgram::draw(). + +While a mesh can be attribute-less and rely on a specialized vertex shader to +generate positions and other data, in most cases it has one or more associated +vertex buffers and corresponding attribute bindings added using +@ref addVertexBuffer(). In the following snippet, a single position attribute +is specified, making it suitable to be rendered with the @ref Shaders::FlatGL +shader. The @relativeref{Magnum,Vector3} type we use for the data matches the +type expected by @ref Shaders::FlatGL3D::Position, so the default constructor +is sufficient for it. The @ref GL-Mesh-configuration-formats section below +shows cases where the types don't match. -If you want indexed mesh, fill your index buffer with data and specify its -layout using @ref setIndexBuffer(). You can also use @ref MeshTools::compressIndices() -to conveniently compress the indices based on the range used. +@snippet MagnumGL.cpp Mesh-vertices -There is also @ref MeshTools::compile() function which operates directly on -@ref Trade::MeshData and returns fully configured mesh and vertex/index buffers -for use with stock shaders. +Here's a mesh with a position and a normal interleaved together, as is needed +for @ref Shaders::PhongGL. See the docs of @ref addVertexBuffer() for +detailed description how particular attributes, offsets and paddings are +specified. Note that @ref Shaders::FlatGL::Position and +@ref Shaders::PhongGL::Position are both aliases to +@ref Shaders::GenericGL::Position, meaning you can render a mesh configured for +the Phong shader with the Flat shader as well: -@attention Note that, by default, neither vertex buffers nor index buffer is - managed (e.g. deleted on destruction) by the mesh, so you have to manage - them on your own and ensure that they are available for whole mesh - lifetime. See @ref GL-Mesh-buffer-ownership for a way to transfer buffer - ownership to the mesh. +@snippet MagnumGL.cpp Mesh-vertices-interleaved -If vertex/index count or instance count is zero, the mesh is empty and no draw -commands are issued when calling @ref AbstractShaderProgram::draw(). +Indexed meshes have the index buffer and corresponding index type set using +@ref setIndexBuffer(). -@subsection GL-Mesh-configuration-example Example mesh configuration +@snippet MagnumGL.cpp Mesh-indices -@subsubsection GL-Mesh-configuration-example-basic Basic non-indexed mesh + -@snippet MagnumGL.cpp Mesh-nonindexed +@m_class{m-note m-warning} -@subsubsection GL-Mesh-configuration-interleaved Interleaved vertex data +@par + Note that, by default, the mesh doesn't deal with buffer ownership. You + have to ensure the index and vertex buffers stay in scope for as long as + the mesh is used, otherwise you'll end up with broken rendering or driver + crashes. See @ref GL-Mesh-buffer-ownership below for a way to transfer + buffer ownership to the mesh. -@snippet MagnumGL.cpp Mesh-interleaved + -@subsubsection GL-Mesh-configuration-indexed Indexed mesh +@m_class{m-note m-success} -@snippet MagnumGL.cpp Mesh-indexed +@par + A basic non-indexed mesh setup using the low-level interface is shown in + the @ref examples-triangle "Triangle example", an indexed mesh then in the + following @ref examples-texturedquad "Textured Quad example". -Or using @ref MeshTools::interleave() and @ref MeshTools::compressIndices(): +@subsection GL-Mesh-configuration-tools Using MeshTools -@snippet MagnumGL.cpp Mesh-indexed-tools +Real-world use cases rarely have a statically defined @cpp struct @ce with the +desired vertex attribute layout. If you have loose attribute arrays, you can +use @ref MeshTools::interleave() to interleave them together. The usage +including the padding specification, is similar to @ref addVertexBuffer(). The +above vertex buffer setup but with separate position and normal arrays that get interleaved can be expressed like this: -Or, if you plan to use the mesh with stock shaders, you can just use -@ref MeshTools::compile(). +@snippet MagnumGL.cpp Mesh-vertices-interleaved-tool -@subsubsection GL-Mesh-configuration-formats Specific formats of vertex data +For indices it's often beneficial to store them in a 16-bit type if they don't +need the full 32-bit range. That's what @ref MeshTools::compressIndices() is +for: + +@snippet MagnumGL.cpp Mesh-indices-tool + +The ultimate generic tool is the already-shown @ref MeshTools::compile(), +together with all @ref MeshTools APIs that operate on @ref Trade::MeshData +instances. See the class documentation for additional ways of accessing and +processing the data contained there. + +@subsection GL-Mesh-configuration-formats Advanced formats of vertex data + +Even though a shader accepts, say, a 32-bit floating-point vector, the actual +mesh data don't need to match that and can be in a smaller type to save on +memory bandwidth. The GPU vertex fetching hardware will then unpack them as +necessary. The following snippet shows a setup similar to the above position + +normal mesh, except that the position is a @relativeref{Magnum,Vector3h} and +the normal is a packed normalized @relativeref{Magnum,Vector3s}, together with +padding for having vertex boundaries aligned to four bytes to make the GPU +happier: @snippet MagnumGL.cpp Mesh-formats -@subsubsection GL-Mesh-configuration-dynamic Dynamically specified attributes +@subsection GL-Mesh-configuration-dynamic Dynamically specified attributes In some cases, for example when the shader code is fully generated at runtime, it's not possible to know attribute locations and types at compile time. In that case, there are overloads of @ref addVertexBuffer() and -@ref addVertexBufferInstanced() that take @ref DynamicAttribute instead of -@ref Attribute typedefs. Adding a RGB attribute at location 3 normalized from -unsigned byte to float with one byte padding at the end could then look like -this: +@ref addVertexBufferInstanced() that take a @ref DynamicAttribute instead of +the @ref Attribute typedefs, however then you're responsible for explicitly +specifying also the stride. Adding a RGB attribute at location 3 normalized +from unsigned byte to float with one byte padding at the end (or, in other +words, stride of four bytes) could then look like this: @snippet MagnumGL.cpp Mesh-dynamic -@section GL-Mesh-buffer-ownership Transferring buffer ownership +The @ref DynamicAttribute also allows @ref VertexFormat to be used for +specifying attribute types instead of the rather verbose +@ref GL::Attribute::Components, @relativeref{GL::Attribute,DataType} and +@relativeref{GL::Attribute,DataOptions} tuple that GL itself accepts. The above +packed position + normal attribute specification would then look like this: + +@snippet MagnumGL.cpp Mesh-formats-vertexformat + +@subsection GL-Mesh-buffer-ownership Transferring buffer ownership If a vertex/index buffer is used only by a single mesh, it's possible to transfer its ownership to the mesh itself to simplify resource management on -the user side. Simply use the @ref addVertexBuffer() / +the application side. Simply use the @ref addVertexBuffer() / @ref addVertexBufferInstanced() and @ref setIndexBuffer() overloads that take -a @ref Buffer as a rvalue: +a @ref Buffer as a rvalue. While this allows you to discard the buffer +instances and pass just the mesh around, it also means you lose a way to access +or update the buffers afterwards. @snippet MagnumGL.cpp Mesh-buffer-ownership -While this allows you to destruct the buffer instances and pass just the mesh -around, this also means you lose a way to access or update the buffers. If -adding the same buffer multiple times or using it for both vertex and index +If adding the same buffer multiple times or using it for both vertex and index data, be sure to transfer the ownership last to avoid the other functions getting only a moved-out instance. For example: @@ -295,16 +369,22 @@ getting only a moved-out instance. For example: @section GL-Mesh-rendering Rendering meshes -Basic workflow is: bind specific framebuffer for drawing (if needed), set up -respective shader (see -@ref GL-AbstractShaderProgram-rendering-workflow "AbstractShaderProgram documentation" -for more information) and call @ref AbstractShaderProgram::draw(). +With a framebuffer bound and a compatible shader set up, it's only a matter of +calling @ref AbstractShaderProgram::draw(): + +@snippet MagnumGL.cpp Mesh-draw @section GL-Mesh-webgl-restrictions WebGL restrictions @ref MAGNUM_TARGET_WEBGL "WebGL" puts some restrictions on vertex buffer layout, see @ref addVertexBuffer() documentation for details. +The @ref Buffer binding restriction transitively affects meshes as well, +requiring @ref Buffer::TargetHint::ElementArray to be used for index buffers. +To simplify debugging, the @ref addVertexBuffer() and @ref setIndexBuffer() +functions checks proper target hint when adding vertex and index buffers under +WebGL. + @section GL-Mesh-performance-optimization Performance optimizations If @gl_extension{ARB,vertex_array_object} (part of OpenGL 3.0), OpenGL ES 3.0, diff --git a/src/Magnum/Trade/MeshData.h b/src/Magnum/Trade/MeshData.h index 438236bcd..44fbcedb4 100644 --- a/src/Magnum/Trade/MeshData.h +++ b/src/Magnum/Trade/MeshData.h @@ -608,6 +608,12 @@ to the GPU. It also won't be able to deal with any custom attributes that the mesh contains. Continue below to see how to achieve a similar effect with lower-level APIs. +@m_class{m-note m-success} + +@par + A generic mesh setup using the high-level utility is used in the + @ref examples-primitives and @ref examples-viewer examples. + @section Trade-MeshData-usage Basic usage The second simplest usage is accessing attributes through the convenience