#ifndef Magnum_Shaders_LineGL_h #define Magnum_Shaders_LineGL_h /* This file is part of Magnum. Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Vladimír Vondruš Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef MAGNUM_TARGET_GLES2 /** @file * @brief Class @ref Magnum::Shaders::LineGL, typedef @ref Magnum::Shaders::LineGL2D, @ref Magnum::Shaders::LineGL3D * @m_since_latest */ #endif #include #include "Magnum/DimensionTraits.h" #include "Magnum/GL/AbstractShaderProgram.h" #include "Magnum/Shaders/GenericGL.h" #include "Magnum/Shaders/glShaderWrapper.h" #include "Magnum/Shaders/visibility.h" #ifndef MAGNUM_TARGET_GLES2 namespace Magnum { namespace Shaders { namespace Implementation { enum class LineGLFlag: UnsignedShort { VertexColor = 1 << 0, ObjectId = 1 << 1, InstancedObjectId = (1 << 2)|ObjectId, InstancedTransformation = 1 << 3, UniformBuffers = 1 << 4, #ifndef MAGNUM_TARGET_WEBGL ShaderStorageBuffers = UniformBuffers|(1 << 6), #endif MultiDraw = UniformBuffers|(1 << 5) }; typedef Containers::EnumSet LineGLFlags; CORRADE_ENUMSET_OPERATORS(LineGLFlags) } /** @brief Line GL shader @m_since_latest Renders lines expanded to quads in screen space. Compared to builtin GPU line rendering, the lines can be of arbitrary width, with configurable join and cap styles, and antialiased independently of MSAA being used or not. @image html shaders-line.png width=256px @experimental @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} @requires_gles30 Requires integer support in shaders which is not available in OpenGL ES 2.0. @requires_webgl20 Requires integer support in shaders which is not available in WebGL 1.0. @section Shaders-LineGL-usage Usage The shader doesn't work with @ref MeshPrimitive::Lines, @ref MeshPrimitive::LineStrip or @ref MeshPrimitive::LineLoop directly, as that would only be implementable with a relatively expensive geometry shader. Instead, it requires the input data to be organized in quads, with @ref Position, @ref PreviousPosition and @ref NextPosition attributes describing point wíth their surroundings, and @ref Annotation with point-specific annotation such as whether given point is a line cap or a join with neighboring segment. The data layout is described in detail in @ref Shaders-LineGL-mesh-representation below, however in practice it's easiest to convert an existing line @ref Trade::MeshData to a form accepted by this shader with @ref MeshTools::generateLines() and then compile it to a @ref GL::Mesh with @ref MeshTools::compileLines(): @snippet Shaders-gl.cpp LineGL-usage For rendering use @ref setTransformationProjectionMatrix(), @ref setColor(), @ref setWidth() and others. It's important to pass viewport size in @ref setViewportSize() as the line width is interpreted relative to it. @snippet Shaders-gl.cpp LineGL-usage2 @subsection Shaders-LineGL-usage-triangulation Line triangulation Each line segment is rendered as a quad consisting of two triangles. Standalone segments have cap style configurable via @ref Configuration::setCapStyle() with the following styles, segments of zero length can be also used to render points. The @m_span{m-label m-default} white @m_endspan lines show generated triangles, the @m_span{m-label m-info} blue @m_endspan line is the actually visible edge between foreground and background: @m_class{m-row} @parblock @m_div{m-col-l-6 m-text-center} @htmlinclude line-cap-butt.svg @ref LineCapStyle::Butt @m_enddiv @m_div{m-col-l-6 m-text-center} @htmlinclude line-cap-square.svg @ref LineCapStyle::Square @m_enddiv @m_div{m-col-l-6 m-text-center} @htmlinclude line-cap-round.svg @ref LineCapStyle::Round @m_enddiv @m_div{m-col-l-6 m-text-center} @htmlinclude line-cap-triangle.svg @ref LineCapStyle::Triangle @m_enddiv @endparblock Joins between consecutive segments in contiguous line strips are expanded to form a gap-less mesh without overlaps. Depending on join style picked in @ref Configuration::setJoinStyle() and angle between the segments the area between points `A`, `B` and `C` may be filled with another triangle: @m_class{m-row} @parblock @m_div{m-col-l-6 m-text-center} @htmlinclude line-join-miter.svg @ref LineJoinStyle::Miter @m_enddiv @m_div{m-col-l-6 m-text-center} @htmlinclude line-join-bevel.svg @ref LineJoinStyle::Bevel @m_enddiv @endparblock @subsection Shaders-LineGL-usage-antialiasing Antialiasing The lines aren't smoothed out by default, use @ref setSmoothness() to pick a tradeoff between the line being aliased and blurry. This is implemented by interpolating between the foreground color and the background, which assumes blending is set up for pre-multiplied alpha. If you're drawing lines on a single-color background, you can @ref setBackgroundColor() to a color matching the background and keep blending disabled, but note that you may get artifacts if the lines are self-overlapping. @snippet Shaders-gl.cpp LineGL-usage-antialiasing @subsection Shaders-LineGL-usage-3d Lines in 3D The 3D variant of this shader renders the geometry with depth values derived from the original line endpoints, however without any perspective shortening applied --- the line width is the same viewport-relative value independently of the depth the point is at. @section Shaders-LineGL-object-id Object ID output The shader supports writing object ID to the framebuffer for object picking or other annotation purposes. Enable it using @ref Flag::ObjectId and set up an integer buffer attached to the @ref ObjectIdOutput attachment. If you have a batch of meshes with different object IDs, enable @ref Flag::InstancedObjectId and supply per-vertex IDs to the @ref ObjectId attribute. The output will contain a sum of the per-vertex ID and ID coming from @ref setObjectId(). The functionality is practically the same as in the @ref FlatGL shader, see @ref Shaders-FlatGL-object-id "its documentation" for more information and usage example. Note that the object ID is emitted for the whole triangle area, including transparent areas of caps when using @ref LineCapStyle::Round or @ref LineCapStyle::Triangle as well as semi-transparent edges with smoothness values larger than zero. In particular, the object ID output will be aliased even if the color output isn't. @section Shaders-LineGL-instancing Instanced rendering Enabling @ref Flag::InstancedTransformation will turn the shader into an instanced one. It'll take per-instance transformation from the @ref TransformationMatrix attribute, applying it before the matrix set by @ref setTransformationProjectionMatrix(). Besides that, @ref Flag::VertexColor (and the @ref Color3 / @ref Color4) attributes can work as both per-vertex and per-instance. The functionality is practically the same as in the @ref FlatGL shader, see @ref Shaders-FlatGL-instancing "its documentation" for more information and usage example. @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} @section Shaders-LineGL-ubo Uniform buffers See @ref shaders-usage-ubo for a high-level overview that applies to all shaders. In this particular case, because the shader doesn't need a separate projection and transformation matrix, a combined one is supplied via a @ref TransformationProjectionUniform2D / @ref TransformationProjectionUniform3D buffer bound with @ref bindTransformationProjectionBuffer(). To maximize use of the limited uniform buffer memory, materials are supplied separately in a @ref LineMaterialUniform buffer bound with @ref bindMaterialBuffer() and then referenced via @relativeref{LineDrawUniform,materialId} from a @ref LineDrawUniform bound with @ref bindDrawBuffer(). A uniform buffer setup equivalent to the @ref Shaders-LineGL-usage "snippet at the top" would look like this --- note that @ref setViewportSize() is an immediate uniform here as well, as it's assumed to be set globally and rarely changed: @snippet Shaders-gl.cpp LineGL-ubo For a multidraw workflow enable @ref Flag::MultiDraw and supply desired material and draw count via @ref Configuration::setMaterialCount() and @relativeref{Configuration,setDrawCount()}. For every draw then specify material references. Besides that, the usage is similar for all shaders, see @ref shaders-usage-multidraw for an example. @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} for uniform buffers. @requires_gl46 Extension @gl_extension{ARB,shader_draw_parameters} for multidraw. @requires_es_extension Extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) (unlisted) for multidraw. @requires_webgl_extension Extension @webgl_extension{ANGLE,multi_draw} for multidraw. @section Shaders-LineGL-mesh-representation Line mesh representation In order to avoid performing expensive CPU-side expansion of the quads every time the transformation, line width and other parameters change, the shader gets just the original line segment endpoints as an input, transforms them in 2D or 3D as usual, and then expands them in screen space for a desired line width. @htmlinclude line-quad-expansion.svg Ignoring all complexity related to line caps and joins for now, an example expansion of three line segments into quads is shown above --- the first two segments form a join at the @m_span{m-label m-info} blue @m_endspan point, the third segment is standalone. In order to form a quad, each of the points has to be present twice in the vertex stream, with first copy expanding up and second copy expanding down. The @ref Position data needed to render quads would then look like below, color-coded to match the above, and in order following the segment direction. An index buffer would then form two triangles out of every four points --- @cpp {0, 1, 2, 2, 1, 3, …} @ce. @htmlinclude line-quad-data.svg To figure out the direction in which to expand, for given endpoint position the shader needs also *screen-space direction* to the other endpoint. But since a 2D / 3D transformation has to be applied for both endpoints before calculating their screen-space position, it makes more sense to supply directly its position instead, and calculate the direction only after transforming both points. The input data would then look like this, with "previous" positions shown above and "next" positions shown below: @htmlinclude line-quad-data-other.svg With line joins and caps present, the quad expansion changes in the following way. In the general case, to avoid overlapping geometry and gaps, points `B` and `D` collapse to a single position and the area in between is filled with an extra triangle. Depending on the transformation, it can however also happen that `A` and `C` collapse into a single point instead, for example if the @m_span{m-label m-primary} azure @m_endspan point would appear above the @m_span{m-label m-success} green @m_endspan one instead of below. Thus the index buffer needs to handle both cases --- @cpp {…, 2, 3, 4, 4, 3, 5, …} @ce --- and one of them always denegerates to a zero-area triangle. @htmlinclude line-quad-expansion-joins-caps.svg To handle the join, the shader needs to know whether there's a neighboring line segment to join with, and what is the position of its other endpoint. Thus, every vertex gets *two* neighboring positions, a @ref PreviousPosition and a @ref NextPosition. Both of them are filled only in case the point forms a line join; if the point is a line cap, one of them is left unspecified. @htmlinclude line-quad-data-neighbor.svg What's left is giving the shader an ability to distinguish the direction in which to expand the point (@ref LineVertexAnnotation::Up or downwards), whether it's a @relativeref{LineVertexAnnotation,Join} or a cap and whether the point is a @relativeref{LineVertexAnnotation,Begin} or an end of the segment in order to know what the neigboring positions represent. This info is stored in the @ref Annotation attribute and shown with `U`, `J` and `B` letters above. In this particular case the info could also be inferred from the vertex index and for example NaNs in the neigbor positions, but a dedicated attribute makes it more flexible for optimized data layouts explained below. @subsection Shaders-LineGL-data-representation-overlap Overlapping layouts with less data redundancy Assuming a 3D line mesh with floating-point position attributes, the @ref Annotation attribute packed into a single byte and @ref MeshIndexType::UnsignedShort indices, a a single contiguous line strip consisting of @f$ n @f$ line segments would need @f$ (4(36 + 1) + 24)n = 172n @f$ bytes of data. In comparison, CPU-side-generated indexed quads would need just @f$ (24 + 18)n + 24 = 42n + 24 @f$ bytes, and a (non-indexed) @ref MeshPrimitive::LineStrip only @f$ 12n + 12 @f$ bytes, which is ~14x less. Fortunately, the position data can be organized in a way that makes it possible to reuse them for previous and next positions as well, by binding the same data again under an offset. There's the following possibilites, each with different tradeoffs depending on the use case. Such data layout variants require no special-casing in the shader, only a different mesh setup, making it possible to pick the best option for each line mesh without having to pay for expensive shader switching. @subsubsection Shaders-LineGL-data-representation-overlap-nojoin Standalone line segments without joins If the mesh consists just of loose line segments and no joints need to be drawn, the @ref Position attribute can be bound with an offset of @cpp -2 @ce elements to the @ref PreviousPosition and @cpp +2 @ce elements to the @ref NextPosition. To avoid out-of-bound reads, the position buffer needs to be padded with two elements at the front and at the end. Together with no indices needed for joint triangles the memory requirement would be reduced to @f$ (4(12 + 1) + 12)n + 12 = 64 n + 12 @f$ bytes, which is roughly the same amount of data as for loose CPU-side-generated indexed quads, and ~2.7x as much as @f$ 24n @f$ bytes a sufficiently large (non-indexed) @ref MeshPrimitive::Lines would need. @m_div{m-row m-container-inflate} @m_div{m-col-l-12 m-nopady m-nopadx} @htmlinclude line-quad-data-overlap-nojoin.svg @m_enddiv @m_enddiv @subsubsection Shaders-LineGL-data-representation-overlap-generic Generic lines For arbitrary lines that consist of both joined strips and standalone segments and the joins can be of any style in any direction, the @ref Position attribute has to be additionally padded with two elements at begin and end of every contiguous line strip together with skipping the elements in the index buffer appropriately, and then bound with an offset of @cpp -4 @ce elements to the @ref PreviousPosition and @cpp +4 @ce elements to the @ref NextPosition. This needs only one triangle in the index buffer for each join instead of two and has a memory requirement of @f$ (4(12 + 1) + 24)n + (4(12 + 1) - 12)l + 12 @f$ bytes, with @f$ l @f$ being the line strip count. With a mesh consisting of just a single strip this is @f$ 76n + 52 @f$ bytes, which is ~1.8x as much as CPU-side-generated indexed quads and ~6.3x as much as a @ref MeshPrimitive::LineStrip would need. @m_div{m-row m-container-inflate} @m_div{m-col-l-12 m-nopady m-nopadx} @htmlinclude line-quad-data-overlap-generic.svg @m_enddiv @m_enddiv @subsubsection Shaders-LineGL-data-representation-overlap-fixedjoin Lines with fixed join directions If the joint direction is known to be fixed, i.e. the B and D points always collapse to the same position independently of the transform used, the two points can be replaced with just one. This is commonly the case in 2D if negative transformation scaling isn't involved and with planar line art in 3D if it additionally also isn't viewed from the back side. This allows padding of the @ref Position attribute at the begin and end of every contiguous line strip to be reduced to just one element, binding it with an offset of @cpp -3 @ce elements to the @ref PreviousPosition and @cpp +3 @ce elements to the @ref NextPosition. This has a memory requirement of @f$ (3(12 + 1) + 18)n + (3(12 + 1) - 6)l + 12 @f$ bytes. With a mesh consisting of just a single strip this is @f$ 57n + 45 @f$ bytes, which is ~1.4x as much as CPU-side-generated indexed quads and ~4.75x as much as a @ref MeshPrimitive::LineStrip would need. @m_div{m-row m-container-inflate} @m_div{m-col-l-12 m-nopady m-nopadx} @htmlinclude line-quad-data-overlap-fixedjoin.svg @m_enddiv @m_enddiv @subsubsection Shaders-LineGL-data-representation-overlap-miterjoin Lines with miter joins only The final and most data-efficient case is for line meshes where the contiguous segments consist of miter joints only (i.e., with the assumption that the angle between two segments is never too sharp to fall back to @ref LineJoinStyle::Bevel), resulting in the join collapsing to just two vertices, with no triangle in between: @htmlinclude line-quad-expansion-joins-miter-caps.svg This is the usual case for finely subdivided curves. Generic line art can be patched in a preprocessing step, subdividing sharp corners to a sequence of joins with larger angles. This layout doesn't require any padding of the @ref Position attribute between contiguous line strips, and it's bound with an offset of @cpp -2 @ce elements to the @ref PreviousPosition and @cpp +2 @ce elements to the @ref NextPosition. The memory requirement is @f$ (2(12 + 1) + 12)n + 2(12 + 1)l + 12 @f$ bytes. With a mesh consisting of a single strip it's @f$ 38n + 38 @f$ bytes. This is roughly the same memory use as @f$ 36n + 24 @f$ bytes for CPU-side-generated quads with miter joins only, and ~3.2x as much as a @ref MeshPrimitive::LineStrip would need. @m_div{m-row m-container-inflate} @m_div{m-col-l-12 m-nopady m-nopadx} @htmlinclude line-quad-data-overlap-miterjoin.svg @m_enddiv @m_enddiv */ template class MAGNUM_SHADERS_EXPORT LineGL: public GL::AbstractShaderProgram { public: /* MSVC needs dllexport here as well */ class MAGNUM_SHADERS_EXPORT Configuration; class CompileState; /** * @brief Vertex position * * @ref shaders-generic "Generic attribute", * @relativeref{Magnum,Vector2} in 2D, @relativeref{Magnum,Vector3} in * 3D. */ typedef typename GenericGL::Position Position; /** * @brief Previous position * * @relativeref{Magnum,Vector2} in 2D, @relativeref{Magnum,Vector3} in * 3D. Uses the same location as @ref GenericGL::Tangent with the * assumption that lines don't need tangent space information. * * If @ref LineVertexAnnotation::Begin is set in @ref Annotation, * contains the other point of the neighboring line segment if * @ref LineVertexAnnotation::Join is also set, and is ignored * otherwise. If @ref LineVertexAnnotation::Begin is not set in * @ref Annotation, contains the other point of the line segment. */ typedef GL::Attribute<3, VectorTypeFor> PreviousPosition; /** * @brief Next position * * @relativeref{Magnum,Vector2} in 2D, @relativeref{Magnum,Vector3} in * 3D. Uses the same location as @ref GenericGL::Normal with the * assumption that lines don't need tangent space information. * * If @ref LineVertexAnnotation::Begin is set in @ref Annotation, * contains the other point of the line segment. If * @ref LineVertexAnnotation::Begin is not set in @ref Annotation, * contains the other point of the neighboring line segment if * @ref LineVertexAnnotation::Join is set, and is ignored otherwise. */ typedef GL::Attribute<5, VectorTypeFor> NextPosition; /** * @brief Vertex annotation * * Uses the same location as @ref GenericGL::TextureCoordinates with * the assumption that lines don't need a two-dimensional texture * space information. * * Contains a set of @ref LineVertexAnnotation bits, see their * documentation for more information. The values are guaranteed to fit * into 8 bits. */ typedef GL::Attribute<1, UnsignedInt> Annotation; /** * @brief Three-component vertex color * * @ref shaders-generic "Generic attribute", @ref Magnum::Color3. Use * either this or the @ref Color4 attribute. */ typedef typename GenericGL::Color3 Color3; /** * @brief Four-component vertex color * * @ref shaders-generic "Generic attribute", @ref Magnum::Color4. Use * either this or the @ref Color3 attribute. */ typedef typename GenericGL::Color4 Color4; /** * @brief (Instanced) object ID * * @ref shaders-generic "Generic attribute", * @relativeref{Magnum,UnsignedInt}. Used only if * @ref Flag::InstancedObjectId is set. */ typedef typename GenericGL::ObjectId ObjectId; /** * @brief (Instanced) transformation matrix * * @ref shaders-generic "Generic attribute", * @relativeref{Magnum,Matrix3} in 2D, @relativeref{Magnum,Matrix4} in * 3D. Used only if @ref Flag::InstancedTransformation is set. * @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} */ typedef typename GenericGL::TransformationMatrix TransformationMatrix; enum: UnsignedInt { /** * Color shader output. Present always, expects three- or * four-component floating-point or normalized buffer attachment. */ ColorOutput = GenericGL::ColorOutput, /** * Object ID shader output. @ref shaders-generic "Generic output", * present only if @ref Flag::ObjectId is set. Expects a * single-component unsigned integral attachment. Writes the value * set in @ref setObjectId() and possibly also a per-vertex ID and * an ID fetched from a texture, see @ref Shaders-LineGL-object-id * for more information. * @requires_gl30 Extension @gl_extension{EXT,texture_integer} */ ObjectIdOutput = GenericGL::ObjectIdOutput }; #ifdef DOXYGEN_GENERATING_OUTPUT /** * @brief Flag * * @see @ref Flags, @ref flags(), @ref Configuration::setFlags() */ enum class Flag: UnsignedShort { /** * Multiply the color with a vertex color. Requires either the * @ref Color3 or @ref Color4 attribute to be present. */ VertexColor = 1 << 0, /** * Enable object ID output. See @ref Shaders-LineGL-object-id for * more information. */ ObjectId = 1 << 1, /** * Instanced object ID. Retrieves a per-instance / per-vertex * object ID from the @ref ObjectId attribute, outputting a sum of * the per-vertex ID and ID coming from @ref setObjectId() or * @ref LineDrawUniform::objectId. Implicitly enables * @ref Flag::ObjectId. See @ref Shaders-LineGL-object-id for more * information. */ InstancedObjectId = (1 << 2)|ObjectId, /** * Instanced transformation. Retrieves a per-instance * transformation matrix from the @ref TransformationMatrix * attribute and uses it together with the matrix coming from * @ref setTransformationProjectionMatrix() or * @ref TransformationProjectionUniform2D::transformationProjectionMatrix * / @ref TransformationProjectionUniform3D::transformationProjectionMatrix * (first the per-instance, then the uniform matrix). See * @ref Shaders-LineGL-instancing for more information. * @requires_gl33 Extension @gl_extension{ARB,instanced_arrays} */ InstancedTransformation = 1 << 3, /** * Use uniform buffers. Expects that uniform data are supplied via * @ref bindTransformationProjectionBuffer(), * @ref bindDrawBuffer() and @ref bindMaterialBuffer() instead of * direct uniform setters. * @see @ref Flag::ShaderStorageBuffers * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} */ UniformBuffers = 1 << 4, #ifndef MAGNUM_TARGET_WEBGL /** * Use shader storage buffers. Superset of functionality provided * by @ref Flag::UniformBuffers, compared to it doesn't have any * size limits on @ref Configuration::setMaterialCount() and * @relativeref{Configuration,setDrawCount()} in exchange for * potentially more costly access and narrower platform support. * @requires_gl43 Extension @gl_extension{ARB,shader_storage_buffer_object} * @requires_gles31 Shader storage buffers are not available in * OpenGL ES 3.0 and older. * @requires_gles Shader storage buffers are not available in * WebGL. * @m_since_latest */ ShaderStorageBuffers = UniformBuffers|(1 << 6), #endif /** * Enable multidraw functionality. Implies @ref Flag::UniformBuffers * and adds the value from @ref setDrawOffset() with the * @glsl gl_DrawID @ce builtin, which makes draws submitted via * @ref GL::AbstractShaderProgram::draw(const Containers::Iterable&) * and related APIs pick up per-draw parameters directly, without * having to rebind the uniform buffers or specify * @ref setDrawOffset() before each draw. In a non-multidraw * scenario, @glsl gl_DrawID @ce is @cpp 0 @ce, which means a * shader with this flag enabled can be used for regular draws as * well. * @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object} * and @gl_extension{ARB,shader_draw_parameters} * @requires_es_extension OpenGL ES 3.0 and extension * @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt) * (unlisted). While the extension alone needs only OpenGL ES * 2.0, the shader implementation relies on uniform buffers, * which require OpenGL ES 3.0. * @requires_webgl_extension WebGL 2.0 and extension * @webgl_extension{ANGLE,multi_draw}. While the extension * alone needs only WebGL 1.0, the shader implementation * relies on uniform buffers, which require WebGL 2.0. */ MultiDraw = UniformBuffers|(1 << 5) }; /** * @brief Flags * * @see @ref flags(), @ref Configuration::setFlags() */ typedef Containers::EnumSet Flags; #else /* Done this way to be prepared for possible future diversion of 2D and 3D flags (e.g. introducing 3D-specific features) */ typedef Implementation::LineGLFlag Flag; typedef Implementation::LineGLFlags Flags; #endif /** * @brief Compile asynchronously * * Compared to @ref LineGL(const Configuration&) can perform an * asynchronous compilation and linking. See @ref shaders-async for * more information. * @see @ref LineGL(CompileState&&) */ /* Compared to the non-templated shaders like PhongGL or MeshVisualizerGL2D using a forward declaration is fine here. Huh. */ static CompileState compile(const Configuration& configuration = Configuration{}); /** @brief Constructor */ /* Compared to the non-templated shaders like PhongGL or MeshVisualizerGL2D using a forward declaration is fine here. Huh. */ explicit LineGL(const Configuration& configuration = Configuration{}); /** * @brief Finalize an asynchronous compilation * * Takes an asynchronous compilation state returned by @ref compile() * and forms a ready-to-use shader object. See @ref shaders-async for * more information. */ explicit LineGL(CompileState&& state); /** * @brief Construct without creating the underlying OpenGL object * * The constructed instance is equivalent to a moved-from state. Useful * in cases where you will overwrite the instance later anyway. Move * another object over it to make it useful. * * This function can be safely used for constructing (and later * destructing) objects even without any OpenGL context being active. * However note that this is a low-level and a potentially dangerous * API, see the documentation of @ref NoCreate for alternatives. */ explicit LineGL(NoCreateT) noexcept: GL::AbstractShaderProgram{NoCreate} {} /** @brief Copying is not allowed */ LineGL(const LineGL&) = delete; /** @brief Move constructor */ LineGL(LineGL&&) noexcept = default; /** @brief Copying is not allowed */ LineGL& operator=(const LineGL&) = delete; /** @brief Move assignment */ LineGL& operator=(LineGL&&) noexcept = default; /** * @brief Flags * * @see @ref Configuration::setFlags() */ Flags flags() const { return _flags; } /** * @brief Cap style * * @see @ref Configuration::setCapStyle() */ LineCapStyle capStyle() const { return _capStyle; } /** * @brief Join style * * @see @ref Configuration::setJoinStyle() */ LineJoinStyle joinStyle() const { return _joinStyle; } /** * @brief Material count * * Statically defined size of the @ref LineMaterialUniform uniform * buffer bound with @ref bindMaterialBuffer(). Has use only if * @ref Flag::UniformBuffers is set and @ref Flag::ShaderStorageBuffers * is not set. * @see @ref Configuration::setMaterialCount() */ UnsignedInt materialCount() const { return _materialCount; } /** * @brief Draw count * * Statically defined size of each of the * @ref TransformationProjectionUniform2D / * @ref TransformationProjectionUniform3D and @ref LineDrawUniform * uniform buffers bound with @ref bindTransformationProjectionBuffer() * and @ref bindDrawBuffer(). Has use only if @ref Flag::UniformBuffers * is set and @ref Flag::ShaderStorageBuffers is not set. * @see @ref Configuration::setDrawCount() */ UnsignedInt drawCount() const { return _drawCount; } /** * @brief Set viewport size * @return Reference to self (for method chaining) * * Line width and smoothness set in either @ref setWidth() / * @ref setSmoothness() or @ref LineMaterialUniform::width / * @ref LineMaterialUniform::smoothness depends on this value --- i.e., * a value of @cpp 1.0f @ce is one pixel only if @ref setViewportSize() * is called with the actual pixel size of the viewport. Initial value * is a zero vector. */ LineGL& setViewportSize(const Vector2& size); /** @{ * @name Uniform setters * * Used only if @ref Flag::UniformBuffers is not set. */ /** * @brief Set transformation and projection matrix * @return Reference to self (for method chaining) * * Initial value is an identity matrix. If * @ref Flag::InstancedTransformation is set, the per-instance * transformation matrix coming from the @ref TransformationMatrix * attribute is applied first, before this one. * * Expects that @ref Flag::UniformBuffers is not set, in that case fill * @ref TransformationProjectionUniform2D::transformationProjectionMatrix / * @ref TransformationProjectionUniform3D::transformationProjectionMatrix * and call @ref bindTransformationProjectionBuffer() instead. */ LineGL& setTransformationProjectionMatrix(const MatrixTypeFor& matrix); /** * @brief Set background color * @return Reference to self (for method chaining) * * Initial value is @cpp 0x00000000_rgbaf @ce. Used for edge smoothing * if smoothness is non-zero, and for background areas if * @ref LineCapStyle::Round or @ref LineCapStyle::Triangle is used. If * smoothness is zero and @ref LineCapStyle::Butt or * @ref LineCapStyle::Square is used, only the foreground color is * used. * * Expects that @ref Flag::UniformBuffers is not set, in that case fill * @ref LineMaterialUniform::backgroundColor and call * @ref bindMaterialBuffer() instead. * @see @ref setColor(), @ref setSmoothness(), * @ref Configuration::setCapStyle() */ LineGL& setBackgroundColor(const Magnum::Color4& color); /** * @brief Set color * @return Reference to self (for method chaining) * * Initial value is @cpp 0xffffffff_rgbaf @ce. * * Expects that @ref Flag::UniformBuffers is not set, in that case fill * @ref LineMaterialUniform::color and call @ref bindMaterialBuffer() * instead. * @see @ref setBackgroundColor() */ LineGL& setColor(const Magnum::Color4& color); /** * @brief Set line width * @return Reference to self (for method chaining) * * Screen-space, interpreted depending on the viewport size --- i.e., * a value of @cpp 1.0f @ce is one pixel only if @ref setViewportSize() * is called with the actual pixel size of the viewport. Initial value * is @cpp 1.0f @ce. * * Expects that @ref Flag::UniformBuffers is not set, in that case fill * @ref LineMaterialUniform::width and call @ref bindMaterialBuffer() * instead. */ LineGL& setWidth(Float width); /** * @brief Set line smoothness * @return Reference to self (for method chaining) * * Larger values will make edges look less aliased (but blurry), * smaller values will make them more crisp (but possibly aliased). * Screen-space, interpreted depending on the viewport size --- i.e., * a value of @cpp 1.0f @ce is one pixel only if @ref setViewportSize() * is called with the actual pixel size of the viewport. Initial value * is @cpp 0.0f @ce. * * Expects that @ref Flag::UniformBuffers is not set, in that case fill * @ref LineMaterialUniform::smoothness and call @ref bindMaterialBuffer() * instead. */ LineGL& setSmoothness(Float smoothness); /** * @brief Set miter length limit * @return Reference to self (for method chaining) * * Maximum length (relative to line width) over which a * @ref LineJoinStyle::Miter join is converted to a * @ref LineJoinStyle::Bevel in order to avoid sharp corners extending * too much. Default value is @cpp 4.0f @ce, which corresponds to * approximately 29 degrees. Alternatively you can set the limit as an * angle using @ref setMiterAngleLimit(). Miter length is calculated * using the following formula, where @f$ w @f$ is line half-width, * @f$ l @f$ is miter length and @f$ \theta @f$ is angle between two * line segments: @f[ * \frac{w}{l} = \sin(\frac{\theta}{2}) * @f] * * Expects that @ref joinStyle() is @ref LineJoinStyle::Miter and * @p limit is greater than or equal to @cpp 1.0f @ce and finite. * Expects that @ref Flag::UniformBuffers is not set, in that case fill * @ref LineMaterialUniform::miterLimit using * @ref LineMaterialUniform::setMiterLengthLimit() and call * @ref bindMaterialBuffer() instead. */ LineGL& setMiterLengthLimit(Float limit); /** * @brief Set miter angle limit * @return Reference to self (for method chaining) * * Like @ref setMiterLengthLimit(), but specified as a minimum angle * between two line segments below which a @ref LineJoinStyle::Miter * join is converted to a @ref LineJoinStyle::Bevel in order to avoid * sharp corners extending too much. Default value is approximately * @cpp 28.955_degf @ce, see @ref setMiterLengthLimit() above for more * information. * * Expects that @ref joinStyle() is @ref LineJoinStyle::Miter and * @p limit is greater than @cpp 0.0_radf @ce. Expects that * @ref Flag::UniformBuffers is not set, in that case fill * @ref LineMaterialUniform::miterLimit using * @ref LineMaterialUniform::setMiterAngleLimit() and call * @ref bindMaterialBuffer() instead. */ LineGL& setMiterAngleLimit(Rad limit); /** * @brief Set object ID * @return Reference to self (for method chaining) * * Expects that the shader was created with @ref Flag::ObjectId * enabled. Value set here is written to the @ref ObjectIdOutput, see * @ref Shaders-LineGL-object-id for more information. Initial value * is @cpp 0 @ce. If @ref Flag::InstancedObjectId is enabled as well, * this value is added to the ID coming from the @ref ObjectId * attribute. * * Expects that @ref Flag::UniformBuffers is not set, in that case fill * @ref LineDrawUniform::objectId and call @ref bindDrawBuffer() * instead. */ LineGL& setObjectId(UnsignedInt id); /** * @} */ /** @{ * @name Uniform / shader storage buffer binding and related uniform setters * * Used if @ref Flag::UniformBuffers is set. */ /** * @brief Bind a draw offset * @return Reference to self (for method chaining) * * Specifies which item in the @ref TransformationProjectionUniform2D / * @ref TransformationProjectionUniform3D and @ref LineDrawUniform * buffers bound with @ref bindTransformationProjectionBuffer() and * @ref bindDrawBuffer() should be used for current draw. Expects that * @ref Flag::UniformBuffers is set and @p offset is less than * @ref drawCount(). Initial value is @cpp 0 @ce, if @ref drawCount() * is @cpp 1 @ce, the function is a no-op as the shader assumes draw * offset to be always zero. * * If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this * value, which makes each draw submitted via * @ref GL::AbstractShaderProgram::draw(const Containers::Iterable&) * pick up its own per-draw parameters. * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} * @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0. * @requires_webgl20 Uniform buffers are not available in WebGL 1.0. */ LineGL& setDrawOffset(UnsignedInt offset); /** * @brief Bind a transformation and projection uniform / shader storage buffer * @return Reference to self (for method chaining) * * Expects that @ref Flag::UniformBuffers is set. The buffer is * expected to contain @ref drawCount() instances of * @ref TransformationProjectionUniform2D / * @ref TransformationProjectionUniform3D. At the very least you need * to call also @ref bindDrawBuffer() and @ref bindMaterialBuffer(). * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} */ LineGL& bindTransformationProjectionBuffer(GL::Buffer& buffer); LineGL& bindTransformationProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /**< @overload */ /** * @brief Bind a draw uniform / shader storage buffer * @return Reference to self (for method chaining) * * Expects that @ref Flag::UniformBuffers is set. The buffer is * expected to contain @ref drawCount() instances of * @ref LineDrawUniform. At the very least you need to call also * @ref bindTransformationProjectionBuffer() and * @ref bindMaterialBuffer(). * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} */ LineGL& bindDrawBuffer(GL::Buffer& buffer); LineGL& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /**< @overload */ /** * @brief Bind a material uniform / shader storage buffer * @return Reference to self (for method chaining) * * Expects that @ref Flag::UniformBuffers is set. The buffer is * expected to contain @ref materialCount() instances of * @ref LineMaterialUniform. At the very least you need to call also * @ref bindTransformationProjectionBuffer() and @ref bindDrawBuffer(). * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} */ LineGL& bindMaterialBuffer(GL::Buffer& buffer); LineGL& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /**< @overload */ /** * @} */ MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION(LineGL) private: /* Creates the GL shader program object but does nothing else. Internal, used by compile(). */ explicit LineGL(NoInitT); Flags _flags; LineCapStyle _capStyle; LineJoinStyle _joinStyle; UnsignedInt _materialCount{}, _drawCount{}; Int _viewportSizeUniform{0}, _transformationProjectionMatrixUniform{1}, _backgroundColorUniform{2}, _colorUniform{3}, _widthUniform{4}, _smoothnessUniform{5}, _miterLimitUniform{6}, _objectIdUniform{7}, /* Used instead of all other uniforms except viewportSize when Flag::UniformBuffers is set, so it can alias them */ _drawOffsetUniform{1}; }; /** @brief Configuration @see @ref LineGL(const Configuration&), @ref compile(const Configuration&) */ template class MAGNUM_SHADERS_EXPORT LineGL::Configuration { public: explicit Configuration(); /** @brief Flags */ Flags flags() const { return _flags; } /** * @brief Set flags * * No flags are set by default. * @see @ref LineGL::flags() */ Configuration& setFlags(Flags flags) { _flags = flags; return *this; } /** @brief Cap style */ LineCapStyle capStyle() const { return _capStyle; } /** * @brief Set cap style * * Unlike for example the SVG specification that uses * @ref LineCapStyle::Butt by default, the default value is * @ref LineCapStyle::Square, in order to make zero-length lines visible. * @see @ref LineGL::capStyle() */ Configuration& setCapStyle(LineCapStyle style) { _capStyle = style; return *this; } /** @brief Join style */ LineJoinStyle joinStyle() const { return _joinStyle; } /** * @brief Set join style * * Default value is @ref LineJoinStyle::Miter, consistently with the * SVG specification. * @see @ref LineGL::joinStyle() */ Configuration& setJoinStyle(LineJoinStyle style) { _joinStyle = style; return *this; } /** @brief Material count */ UnsignedInt materialCount() const { return _materialCount; } /** * @brief Set material count * * If @ref Flag::UniformBuffers is set, describes size of a * @ref LineMaterialUniform buffer bound with * @ref bindMaterialBuffer(). Uniform buffers have a statically defined * size and @cpp count*sizeof(LineMaterialUniform) @ce has to be within * @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if * @ref Flag::ShaderStorageBuffers is set as well, the buffer is * unbounded and @p count is ignored. The per-draw materials are * specified via @ref LineDrawUniform::materialId. Default value is * @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setDrawCount(), * @ref LineGL::materialCount() * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} */ Configuration& setMaterialCount(UnsignedInt count) { _materialCount = count; return *this; } /** @brief Draw count */ UnsignedInt drawCount() const { return _drawCount; } /** * @brief Set draw count * * If @ref Flag::UniformBuffers is set, describes size of a * @ref TransformationProjectionUniform2D / * @ref TransformationProjectionUniform3D / * @ref LineDrawUniform buffer bound with * @ref bindTransformationProjectionBuffer() and @ref bindDrawBuffer(). * Uniform buffers have a statically defined size and the maximum of * @cpp count*sizeof(TransformationProjectionUniform2D) @ce / * @cpp count*sizeof(TransformationProjectionUniform3D) @ce and * @cpp count*sizeof(LineDrawUniform) @ce has to be within * @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if * @ref Flag::ShaderStorageBuffers is set as well, the buffers are * unbounded and @p count is ignored. The draw offset is set via * @ref setDrawOffset(). Default value is @cpp 1 @ce. * * If @ref Flag::UniformBuffers isn't set, this value is ignored. * @see @ref setFlags(), @ref setMaterialCount(), * @ref LineGL::drawCount() * @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} */ Configuration& setDrawCount(UnsignedInt count) { _drawCount = count; return *this; } private: Flags _flags; LineCapStyle _capStyle; LineJoinStyle _joinStyle; UnsignedInt _materialCount{1}; UnsignedInt _drawCount{1}; }; /** @brief Asynchronous compilation state Returned by @ref compile(). See @ref shaders-async for more information. */ template class LineGL::CompileState: public LineGL { /* Everything deliberately private except for the inheritance */ friend class LineGL; explicit CompileState(NoCreateT): LineGL{NoCreate}, _vert{NoCreate}, _frag{NoCreate} {} explicit CompileState(LineGL&& shader, GL::Shader&& vert, GL::Shader&& frag #if !defined(MAGNUM_TARGET_GLES) || !defined(MAGNUM_TARGET_WEBGL) , GL::Version version #endif ): LineGL{Utility::move(shader)}, _vert{Utility::move(vert)}, _frag{Utility::move(frag)} #if !defined(MAGNUM_TARGET_GLES) || !defined(MAGNUM_TARGET_WEBGL) , _version{version} #endif {} Implementation::GLShaderWrapper _vert, _frag; #if !defined(MAGNUM_TARGET_GLES) || !defined(MAGNUM_TARGET_WEBGL) GL::Version _version; #endif }; /** @brief 2D line OpenGL shader @m_since_latest */ typedef LineGL<2> LineGL2D; /** @brief 3D LineGL OpenGL shader @m_since_latest */ typedef LineGL<3> LineGL3D; #ifdef DOXYGEN_GENERATING_OUTPUT /** * @debugoperatorclassenum{LineGL,LineGL::Flag} * @m_since_latest */ template Debug& operator<<(Debug& debug, LineGL::Flag value); /** * @debugoperatorclassenum{LineGL,LineGL::Flags} * @m_since_latest */ template Debug& operator<<(Debug& debug, LineGL::Flags value); #else namespace Implementation { MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, LineGLFlag value); MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, LineGLFlags value); } #endif }} #else #error this header is not available in the OpenGL ES 2.0 / WebGL 1.0 build #endif #endif