Browse Source

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.
pull/525/head
Vladimír Vondruš 5 years ago
parent
commit
491b724a86
  1. 258
      doc/snippets/MagnumGL.cpp
  2. 1
      doc/snippets/MagnumTrade.cpp
  3. 45
      src/Magnum/GL/Buffer.h
  4. 170
      src/Magnum/GL/Mesh.h
  5. 6
      src/Magnum/Trade/MeshData.h

258
doc/snippets/MagnumGL.cpp

@ -47,11 +47,14 @@
#include "Magnum/GL/Texture.h" #include "Magnum/GL/Texture.h"
#include "Magnum/GL/TextureFormat.h" #include "Magnum/GL/TextureFormat.h"
#include "Magnum/GL/Version.h" #include "Magnum/GL/Version.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Math/Half.h"
#include "Magnum/Math/Matrix4.h" #include "Magnum/Math/Matrix4.h"
#include "Magnum/MeshTools/Interleave.h" #include "Magnum/MeshTools/Interleave.h"
#include "Magnum/MeshTools/CompressIndices.h" #include "Magnum/MeshTools/CompressIndices.h"
#include "Magnum/Primitives/Cube.h" #include "Magnum/Primitives/Cube.h"
#include "Magnum/Primitives/Plane.h" #include "Magnum/Primitives/Plane.h"
#include "Magnum/Shaders/FlatGL.h"
#include "Magnum/Shaders/PhongGL.h" #include "Magnum/Shaders/PhongGL.h"
#include "Magnum/Trade/MeshData.h" #include "Magnum/Trade/MeshData.h"
@ -82,6 +85,8 @@
#include "Magnum/GL/RectangleTexture.h" #include "Magnum/GL/RectangleTexture.h"
#endif #endif
#define DOXYGEN_IGNORE(...) __VA_ARGS__
using namespace Magnum; using namespace Magnum;
using namespace Magnum::Math::Literals; using namespace Magnum::Math::Literals;
@ -446,9 +451,12 @@ GL::BufferImage2D image = framebuffer.read(framebuffer.viewport(),
#endif #endif
{ {
GL::Buffer buffer;
/* [Buffer-setdata] */ /* [Buffer-setdata] */
Containers::ArrayView<Vector3> data; const Vector3 data[]{
DOXYGEN_IGNORE({})
};
GL::Buffer buffer;
buffer.setData(data); buffer.setData(data);
GL::Buffer buffer2{data}; // or construct & fill in a single step 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; GL::Buffer buffer;
/* [Buffer-setdata-stl] */ /* [Buffer-setdata-stl] */
std::vector<Vector3> data; std::vector<Vector3> data = DOXYGEN_IGNORE({});
buffer.setData(data); buffer.setData(data);
/* [Buffer-setdata-stl] */ /* [Buffer-setdata-stl] */
}
#ifndef MAGNUM_TARGET_GLES
{
Containers::ArrayView<const void> data;
/* [Buffer-setstorage] */
GL::Buffer buffer;
buffer.setStorage(data, {});
/* [Buffer-setstorage] */
}
#endif
{
GL::Buffer buffer;
/* [Buffer-setdata-allocate] */ /* [Buffer-setdata-allocate] */
buffer.setData({nullptr, 200*sizeof(Vector3)}); buffer.setData({nullptr, 200*sizeof(Vector3)});
/* [Buffer-setdata-allocate] */ /* [Buffer-setdata-allocate] */
@ -471,8 +492,9 @@ buffer.setData({nullptr, 200*sizeof(Vector3)});
{ {
GL::Buffer buffer; GL::Buffer buffer;
/* [Buffer-map] */ /* [Buffer-map] */
Containers::ArrayView<Vector3> data = Containers::arrayCast<Vector3>(buffer.map(0, Containers::ArrayView<Vector3> data = Containers::arrayCast<Vector3>(
200*sizeof(Vector3), GL::Buffer::MapFlag::Write|GL::Buffer::MapFlag::InvalidateBuffer)); buffer.map(0, 200*sizeof(Vector3),
GL::Buffer::MapFlag::Write|GL::Buffer::MapFlag::InvalidateBuffer));
CORRADE_INTERNAL_ASSERT(data); CORRADE_INTERNAL_ASSERT(data);
for(Vector3& d: data) for(Vector3& d: data)
d = {/*...*/}; d = {/*...*/};
@ -483,8 +505,9 @@ CORRADE_INTERNAL_ASSERT_OUTPUT(buffer.unmap());
{ {
GL::Buffer buffer; GL::Buffer buffer;
/* [Buffer-flush] */ /* [Buffer-flush] */
Containers::ArrayView<Vector3> data = Containers::arrayCast<Vector3>(buffer.map(0, Containers::ArrayView<Vector3> data = Containers::arrayCast<Vector3>(
200*sizeof(Vector3), GL::Buffer::MapFlag::Write|GL::Buffer::MapFlag::FlushExplicit)); buffer.map(0, 200*sizeof(Vector3),
GL::Buffer::MapFlag::Write|GL::Buffer::MapFlag::FlushExplicit));
CORRADE_INTERNAL_ASSERT(data); CORRADE_INTERNAL_ASSERT(data);
for(std::size_t i: {7, 27, 56, 128}) { for(std::size_t i: {7, 27, 56, 128}) {
data[i] = {/*...*/}; data[i] = {/*...*/};
@ -497,14 +520,15 @@ CORRADE_INTERNAL_ASSERT_OUTPUT(buffer.unmap());
{ {
/* [Buffer-webgl-nope] */ /* [Buffer-webgl-nope] */
GL::Buffer vertices, indices; GL::Buffer vertices;
GL::Buffer indices;
/* [Buffer-webgl-nope] */ /* [Buffer-webgl-nope] */
} }
{ {
/* [Buffer-webgl] */ /* [Buffer-webgl] */
GL::Buffer vertices{GL::Buffer::TargetHint::Array}, GL::Buffer vertices{GL::Buffer::TargetHint::Array};
indices{GL::Buffer::TargetHint::ElementArray}; GL::Buffer indices{GL::Buffer::TargetHint::ElementArray};
/* [Buffer-webgl] */ /* [Buffer-webgl] */
} }
@ -1016,156 +1040,112 @@ framebuffer.mapForDraw({
#endif #endif
{ {
/* [Mesh-nonindexed] */ /* [Mesh-vertices] */
/* Custom shader, needing only position data */ const Vector3 positions[]{
class MyShader: public GL::AbstractShaderProgram { DOXYGEN_IGNORE({})
public:
typedef GL::Attribute<0, Vector3> Position;
// ...
};
/* Fill vertex buffer with position data */
Vector3 positions[30]{
// ...
}; };
GL::Buffer vertexBuffer; GL::Buffer vertices{positions};
vertexBuffer.setData(positions, GL::BufferUsage::StaticDraw);
/* Configure the mesh, add vertex buffer */
GL::Mesh mesh; GL::Mesh mesh;
mesh.setPrimitive(MeshPrimitive::Triangles) mesh.setCount(Containers::arraySize(positions))
.addVertexBuffer(vertexBuffer, 0, MyShader::Position{}) .addVertexBuffer(vertices, 0, Shaders::FlatGL3D::Position{});
.setCount(30); /* [Mesh-vertices] */
/* [Mesh-nonindexed] */
} }
{ {
/* [Mesh-interleaved] */ /* [Mesh-vertices-interleaved] */
/* Non-indexed primitive with positions and normals */ struct Vertex {
Trade::MeshData plane = Primitives::planeSolid(); Vector3 position;
Vector3 normal;
/* Fill a vertex buffer with interleaved position and normal data */ };
GL::Buffer buffer; const Vertex vertexData[]{
buffer.setData(MeshTools::interleave(plane.positions3DAsArray(), DOXYGEN_IGNORE({})
plane.normalsAsArray())); };
GL::Buffer vertices{vertexData};
/* Configure the mesh, add the vertex buffer */
GL::Mesh mesh; GL::Mesh mesh;
mesh.setPrimitive(plane.primitive()) mesh.setCount(Containers::arraySize(vertexData))
.setCount(plane.vertexCount()) .addVertexBuffer(vertices, 0,
.addVertexBuffer(buffer, 0, Shaders::PhongGL::Position{}, Shaders::PhongGL::Normal{}); Shaders::PhongGL::Position{},
/* [Mesh-interleaved] */ Shaders::PhongGL::Normal{});
/* [Mesh-vertices-interleaved] */
} }
{ {
/* [Mesh-indexed] */ GL::Mesh mesh;
/* Custom shader */ /* [Mesh-indices] */
class MyShader: public GL::AbstractShaderProgram { const UnsignedInt indexData[]{
public: DOXYGEN_IGNORE(0)
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::Buffer indexBuffer; GL::Buffer indices{indexData};
indexBuffer.setData(indices);
/* Configure the mesh, add both vertex and index buffer */ DOXYGEN_IGNORE()
GL::Mesh mesh; mesh.setIndexBuffer(indices, 0, MeshIndexType::UnsignedInt);
mesh.setPrimitive(MeshPrimitive::Triangles) /* [Mesh-indices] */
.setCount(75)
.addVertexBuffer(vertexBuffer, 0, MyShader::Position{})
.setIndexBuffer(indexBuffer, 0, GL::MeshIndexType::UnsignedByte, 176, 229);
/* [Mesh-indexed] */
} }
{ {
/* [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<char> 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; GL::Mesh mesh;
mesh.setPrimitive(cube.primitive()) /* [Mesh-vertices-interleaved-tool] */
.setCount(cube.indexCount()) Containers::ArrayView<const Vector3> positions = DOXYGEN_IGNORE({});
.addVertexBuffer(vertexBuffer, 0, Shaders::PhongGL::Position{}, Shaders::PhongGL::Normal{}) Containers::ArrayView<const Vector3> normals = DOXYGEN_IGNORE({});
.setIndexBuffer(indexBuffer, 0, indexType); GL::Buffer vertices{MeshTools::interleave(positions, normals)};
/* [Mesh-indexed-tools] */
DOXYGEN_IGNORE()
mesh.addVertexBuffer(vertices, 0,
Shaders::PhongGL::Position{},
Shaders::PhongGL::Normal{});
/* [Mesh-vertices-interleaved-tool] */
} }
#ifndef MAGNUM_TARGET_GLES
{ {
/* [Mesh-formats] */ GL::Mesh mesh;
// Custom shader with colors specified as four floating-point values const UnsignedInt indexData[1]{};
class MyShader: public GL::AbstractShaderProgram { /* [Mesh-indices-tool] */
public: Containers::Array<char> compressed;
typedef GL::Attribute<0, Vector3> Position; MeshIndexType type;
typedef GL::Attribute<1, Color4> Color; 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; GL::Mesh mesh;
mesh.setPrimitive(MeshPrimitive::Triangles) /* [Mesh-formats] */
.setCount(30); struct Packed {
Vector3h position;
/* Fill position buffer with positions specified as two-component XY (i.e., Short:16;
no Z component, which is meant to be always 0) */ Vector3s normal;
Vector2 positions[30]{ Short:16;
// ...
}; };
GL::Buffer positionBuffer; const Packed vertexData[]{
positionBuffer.setData(positions, GL::BufferUsage::StaticDraw); DOXYGEN_IGNORE({})
/* 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]{
// ...
}; };
GL::Buffer colorBuffer; GL::Buffer vertices{vertexData};
colorBuffer.setData(colors, GL::BufferUsage::StaticDraw);
DOXYGEN_IGNORE()
/* Specify color buffer layout -- BGRA, each component unsigned byte and we mesh.addVertexBuffer(vertices, 0,
want to normalize them from [0, 255] to [0.0f, 1.0f] */ Shaders::PhongGL::Position{Shaders::PhongGL::Position::Components::Three,
mesh.addVertexBuffer(colorBuffer, 0, MyShader::Color{ Shaders::PhongGL::Position::DataType::Half},
MyShader::Color::Components::BGRA, 2,
MyShader::Color::DataType::UnsignedByte, Shaders::PhongGL::Normal{Shaders::PhongGL::Normal::Components::Three,
MyShader::Color::DataOption::Normalized}); Shaders::PhongGL::Normal::DataType::Short,
Shaders::PhongGL::Normal::DataOption::Normalized},
2);
/* [Mesh-formats] */ /* [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; GL::Mesh mesh;
@ -1182,7 +1162,7 @@ mesh.addVertexBuffer(colorBuffer, 0, 4, GL::DynamicAttribute{
GL::Mesh mesh; GL::Mesh mesh;
/* [Mesh-buffer-ownership] */ /* [Mesh-buffer-ownership] */
GL::Buffer vertices, indices; GL::Buffer vertices, indices;
// ... DOXYGEN_IGNORE()
mesh.addVertexBuffer(std::move(vertices), 0, mesh.addVertexBuffer(std::move(vertices), 0,
Shaders::PhongGL::Position{}, Shaders::PhongGL::Position{},
Shaders::PhongGL::Normal{}) Shaders::PhongGL::Normal{})
@ -1195,6 +1175,16 @@ mesh.addVertexBuffer(vertices, 0, Shaders::PhongGL::Position{}, 20)
/* [Mesh-buffer-ownership-multiple] */ /* [Mesh-buffer-ownership-multiple] */
} }
{
GL::Mesh mesh;
/* [Mesh-draw] */
Shaders::PhongGL shader{DOXYGEN_IGNORE()};
DOXYGEN_IGNORE()
shader.draw(mesh);
/* [Mesh-draw] */
}
{ {
/* [Mesh-addVertexBuffer1] */ /* [Mesh-addVertexBuffer1] */
GL::Buffer buffer; GL::Buffer buffer;

1
doc/snippets/MagnumTrade.cpp

@ -657,6 +657,7 @@ Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal,
#ifdef MAGNUM_TARGET_GL #ifdef MAGNUM_TARGET_GL
{ {
/* This snippet is also used by GL::Mesh, bear that in mind when updating */
/* [MeshData-usage-compile] */ /* [MeshData-usage-compile] */
Trade::MeshData data = DOXYGEN_IGNORE(Trade::MeshData{MeshPrimitive::Points, 0}); Trade::MeshData data = DOXYGEN_IGNORE(Trade::MeshData{MeshPrimitive::Points, 0});

45
src/Magnum/GL/Buffer.h

@ -135,23 +135,36 @@ namespace Implementation { struct BufferState; }
/** /**
@brief Buffer @brief Buffer
Encapsulates one OpenGL buffer object and provides functions for convenient Wraps an OpenGL buffer object.
data updates.
@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() Data can be uploaded to the buffer with @ref setData(), @ref setSubData() or
or the shorthand @ref Buffer() constructor is to use the shorthand @ref Buffer(Containers::ArrayView<const void>, BufferUsage) "Buffer()"
@ref Corrade::Containers::ArrayView. See its documentation for more information constructor. All these functions accept a @relativeref{Corrade,Containers::ArrayView}, which is among other things
about automatic conversions etc. 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 @snippet MagnumGL.cpp Buffer-setdata
If you @cpp #include @ce @ref Corrade/Containers/ArrayViewStl.h, you can also Furthermore, if you @cpp #include @ce @ref Corrade/Containers/ArrayViewStl.h,
pass directly STL types such as @ref std::vector or @link std::array @endlink: 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 @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 @section GL-Buffer-data-mapping Memory mapping
Buffer data can be also updated asynchronously. First you need to allocate 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 target, i.e., @ref Buffer bound to @ref Buffer::TargetHint::Array cannot be
later rebound to @ref Buffer::TargetHint::ElementArray. However, Magnum by later rebound to @ref Buffer::TargetHint::ElementArray. However, Magnum by
default uses any sufficient target when binding the buffer internally (e.g. for 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 setting data). Which means the following, while completely fine on desktop and
desktop, is not sufficient on WebGL: OpenGL ES, is not sufficient on WebGL:
@snippet MagnumGL.cpp Buffer-webgl-nope @snippet MagnumGL.cpp Buffer-webgl-nope
You have to set target hint to desired target, either in constructor or using To avoid GL errors, you have to set target hint to desired target, either in
@ref Buffer::setTargetHint() like this (and similarly for other bufffer types the constructor or using @ref setTargetHint(). A similar care needs to be taken
such as UBOs): for uniform buffers and other types of buffers.
@snippet MagnumGL.cpp Buffer-webgl @snippet MagnumGL.cpp Buffer-webgl
To simplify debugging, @ref Mesh checks proper target hint when adding vertex To simplify debugging, the @ref Mesh class checks proper target hint when
and index buffers in WebGL. adding vertex and index buffers under WebGL.
@section GL-Buffer-performance-optimizations Performance optimizations @section GL-Buffer-performance-optimizations Performance optimizations

170
src/Magnum/GL/Mesh.h

@ -213,81 +213,155 @@ template<class T> struct IsDynamicAttribute<T, DynamicAttribute>: std::true_type
/** /**
@brief Mesh @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 @section GL-Mesh-configuration Mesh configuration
You have to specify at least primitive and vertex/index count using A mesh is, at the very least, a @ref MeshPrimitive and associated vertex/index
@ref setPrimitive() and @ref setCount(). Then fill your vertex buffers with count. To prevent accidentally drawing empty meshes, you're required to call
data, add them to the mesh and specify @ref Attribute "shader attribute" layout @ref setCount() always, the primitive is however implicitly
inside the buffers using @ref addVertexBuffer(). You can also use @ref MeshPrimitive::Triangles and you can change it either in the constructor
@ref MeshTools::interleave() to conveniently interleave vertex data. 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 @snippet MagnumGL.cpp Mesh-vertices
layout using @ref setIndexBuffer(). You can also use @ref MeshTools::compressIndices()
to conveniently compress the indices based on the range used.
There is also @ref MeshTools::compile() function which operates directly on Here's a mesh with a position and a normal interleaved together, as is needed
@ref Trade::MeshData and returns fully configured mesh and vertex/index buffers for @ref Shaders::PhongGL. See the docs of @ref addVertexBuffer() for
for use with stock shaders. 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 @snippet MagnumGL.cpp Mesh-vertices-interleaved
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.
If vertex/index count or instance count is zero, the mesh is empty and no draw Indexed meshes have the index buffer and corresponding index type set using
commands are issued when calling @ref AbstractShaderProgram::draw(). @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 <b></b>
@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 <b></b>
@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 @snippet MagnumGL.cpp Mesh-vertices-interleaved-tool
@ref MeshTools::compile().
@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 @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, 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 it's not possible to know attribute locations and types at compile time. In
that case, there are overloads of @ref addVertexBuffer() and that case, there are overloads of @ref addVertexBuffer() and
@ref addVertexBufferInstanced() that take @ref DynamicAttribute instead of @ref addVertexBufferInstanced() that take a @ref DynamicAttribute instead of
@ref Attribute typedefs. Adding a RGB attribute at location 3 normalized from the @ref Attribute typedefs, however then you're responsible for explicitly
unsigned byte to float with one byte padding at the end could then look like specifying also the stride. Adding a RGB attribute at location 3 normalized
this: 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 @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 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 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 @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 @snippet MagnumGL.cpp Mesh-buffer-ownership
While this allows you to destruct the buffer instances and pass just the mesh If adding the same buffer multiple times or using it for both vertex and index
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
data, be sure to transfer the ownership last to avoid the other functions data, be sure to transfer the ownership last to avoid the other functions
getting only a moved-out instance. For example: 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 @section GL-Mesh-rendering Rendering meshes
Basic workflow is: bind specific framebuffer for drawing (if needed), set up With a framebuffer bound and a compatible shader set up, it's only a matter of
respective shader (see calling @ref AbstractShaderProgram::draw():
@ref GL-AbstractShaderProgram-rendering-workflow "AbstractShaderProgram documentation"
for more information) and call @ref AbstractShaderProgram::draw(). @snippet MagnumGL.cpp Mesh-draw
@section GL-Mesh-webgl-restrictions WebGL restrictions @section GL-Mesh-webgl-restrictions WebGL restrictions
@ref MAGNUM_TARGET_WEBGL "WebGL" puts some restrictions on vertex buffer @ref MAGNUM_TARGET_WEBGL "WebGL" puts some restrictions on vertex buffer
layout, see @ref addVertexBuffer() documentation for details. 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 @section GL-Mesh-performance-optimization Performance optimizations
If @gl_extension{ARB,vertex_array_object} (part of OpenGL 3.0), OpenGL ES 3.0, If @gl_extension{ARB,vertex_array_object} (part of OpenGL 3.0), OpenGL ES 3.0,

6
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 mesh contains. Continue below to see how to achieve a similar effect with
lower-level APIs. 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 @section Trade-MeshData-usage Basic usage
The second simplest usage is accessing attributes through the convenience The second simplest usage is accessing attributes through the convenience

Loading…
Cancel
Save