The shader requires the input data to be laid out in a rather specific way, and there will be a dedicated MeshTools utility for it in the following commits. For independence though, the shader tests use a custom helper. The initial implementation has certain corner cases which will be eventually resolved. For now they are pinned down with repro cases in the test. But apart from that, it's pretty much usable in practice. Remaining join styles (round and miter-clip) as well as stipple support will eventually follow as well.pull/601/head
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
@ -0,0 +1,55 @@
|
||||
#ifndef Magnum_Shaders_Implementation_lineMiterLimit_h |
||||
#define Magnum_Shaders_Implementation_lineMiterLimit_h |
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
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. |
||||
*/ |
||||
|
||||
#include "Magnum/Math/Functions.h" |
||||
|
||||
namespace Magnum { namespace Shaders { namespace Implementation { |
||||
|
||||
inline Float lineMiterLengthLimit(const char* const name, const Float limit) { |
||||
#ifdef CORRADE_NO_ASSERT |
||||
static_cast<void>(name); |
||||
#endif |
||||
CORRADE_ASSERT(limit >= 1.0f && !Math::isInf(limit), |
||||
name << "expected a finite value greater than or equal to 1, got" << limit, {}); |
||||
/* Calculate the half-angle from the length and return a cosine of it */ |
||||
return Math::cos(2.0f*Math::asin(1.0f/limit)); |
||||
} |
||||
|
||||
inline Float lineMiterAngleLimit(const char* const name, const Rad limit) { |
||||
using namespace Math::Literals; |
||||
#ifdef CORRADE_NO_ASSERT |
||||
static_cast<void>(name); |
||||
#endif |
||||
CORRADE_ASSERT(limit > 0.0_radf && limit <= Rad{Constants::pi()}, |
||||
name << "expected a value greater than 0° and less than or equal to 180°, got" << Float(Deg(limit)) << Debug::nospace << "°", {}); |
||||
/* Return a cosine of the angle */ |
||||
return Math::cos(limit); |
||||
} |
||||
|
||||
}}} |
||||
|
||||
#endif |
||||
@ -0,0 +1,76 @@
|
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
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. |
||||
*/ |
||||
|
||||
#include "Line.h" |
||||
|
||||
#include <Corrade/Containers/String.h> |
||||
|
||||
#include "Magnum/Shaders/Implementation/lineMiterLimit.h" |
||||
|
||||
namespace Magnum { namespace Shaders { |
||||
|
||||
LineMaterialUniform& LineMaterialUniform::setMiterLengthLimit(const Float limit) { |
||||
miterLimit = Implementation::lineMiterLengthLimit("Shaders::LineMaterialUniform::setMiterLengthLimit():", limit); |
||||
return *this; |
||||
} |
||||
|
||||
LineMaterialUniform& LineMaterialUniform::setMiterAngleLimit(const Rad limit) { |
||||
miterLimit = Implementation::lineMiterAngleLimit("Shaders::LineMaterialUniform::setMiterAngleLimit():", limit); |
||||
return *this; |
||||
} |
||||
|
||||
Debug& operator<<(Debug& debug, const LineCapStyle value) { |
||||
debug << "Shaders::LineCapStyle" << Debug::nospace; |
||||
|
||||
switch(value) { |
||||
/* LCOV_EXCL_START */ |
||||
#define _c(v) case LineCapStyle::v: return debug << "::" #v; |
||||
_c(Butt) |
||||
_c(Square) |
||||
_c(Round) |
||||
_c(Triangle) |
||||
#undef _c |
||||
/* LCOV_EXCL_STOP */ |
||||
} |
||||
|
||||
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedByte(value)) << Debug::nospace << ")"; |
||||
} |
||||
|
||||
Debug& operator<<(Debug& debug, const LineJoinStyle value) { |
||||
debug << "Shaders::LineJoinStyle" << Debug::nospace; |
||||
|
||||
switch(value) { |
||||
/* LCOV_EXCL_START */ |
||||
#define _c(v) case LineJoinStyle::v: return debug << "::" #v; |
||||
_c(Miter) |
||||
_c(Bevel) |
||||
#undef _c |
||||
/* LCOV_EXCL_STOP */ |
||||
} |
||||
|
||||
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedByte(value)) << Debug::nospace << ")"; |
||||
} |
||||
|
||||
}} |
||||
@ -0,0 +1,257 @@
|
||||
/* |
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
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. |
||||
*/ |
||||
|
||||
#if defined(OBJECT_ID) && !defined(GL_ES) && !defined(NEW_GLSL) |
||||
#extension GL_EXT_gpu_shader4: require |
||||
#endif |
||||
|
||||
/* See the corresponding block in Line.vert for more information */ |
||||
#ifndef GL_ES |
||||
#define CAN_USE_NOPERSPECTIVE |
||||
#elif defined(GL_ES) && defined(GL_NV_shader_noperspective_interpolation) |
||||
#extension GL_NV_shader_noperspective_interpolation: require |
||||
#define CAN_USE_NOPERSPECTIVE |
||||
#endif |
||||
|
||||
#ifndef NEW_GLSL |
||||
#define fragmentColor gl_FragColor |
||||
#define in varying |
||||
#endif |
||||
|
||||
#ifndef RUNTIME_CONST |
||||
#define const |
||||
#endif |
||||
|
||||
/* Uniforms */ |
||||
|
||||
#ifndef UNIFORM_BUFFERS |
||||
#ifdef EXPLICIT_UNIFORM_LOCATION |
||||
layout(location = 2) |
||||
#endif |
||||
uniform lowp vec4 backgroundColor |
||||
#ifndef GL_ES |
||||
= vec4(0.0) |
||||
#endif |
||||
; |
||||
|
||||
#ifdef EXPLICIT_UNIFORM_LOCATION |
||||
layout(location = 3) |
||||
#endif |
||||
uniform lowp vec4 color |
||||
#ifndef GL_ES |
||||
= vec4(1.0) |
||||
#endif |
||||
; |
||||
|
||||
#ifdef EXPLICIT_UNIFORM_LOCATION |
||||
layout(location = 4) |
||||
#endif |
||||
uniform mediump float width |
||||
#ifndef GL_ES |
||||
= 1.0 |
||||
#endif |
||||
; |
||||
|
||||
#ifdef EXPLICIT_UNIFORM_LOCATION |
||||
layout(location = 5) |
||||
#endif |
||||
uniform mediump float smoothness |
||||
#ifndef GL_ES |
||||
= 0.0 |
||||
#endif |
||||
; |
||||
|
||||
#ifdef OBJECT_ID |
||||
#ifdef EXPLICIT_UNIFORM_LOCATION |
||||
layout(location = 7) |
||||
#endif |
||||
/* mediump is just 2^10, which might not be enough, this is 2^16 */ |
||||
uniform highp uint objectId; /* defaults to zero */ |
||||
#endif |
||||
|
||||
/* Uniform buffers */ |
||||
|
||||
#else |
||||
#ifndef MULTI_DRAW |
||||
#if DRAW_COUNT > 1 |
||||
#ifdef EXPLICIT_UNIFORM_LOCATION |
||||
layout(location = 1) |
||||
#endif |
||||
uniform highp uint drawOffset |
||||
#ifndef GL_ES |
||||
= 0u |
||||
#endif |
||||
; |
||||
#else |
||||
#define drawOffset 0u |
||||
#endif |
||||
#define drawId drawOffset |
||||
#endif |
||||
|
||||
struct DrawUniform { |
||||
highp uvec4 materialIdReservedObjectIdReservedReserved; |
||||
#define draw_materialIdReserved materialIdReservedObjectIdReservedReserved.x |
||||
#define draw_objectId materialIdReservedObjectIdReservedReserved.y |
||||
}; |
||||
|
||||
layout(std140 |
||||
#ifdef EXPLICIT_BINDING |
||||
, binding = 2 |
||||
#endif |
||||
) uniform Draw { |
||||
DrawUniform draws[DRAW_COUNT]; |
||||
}; |
||||
|
||||
struct MaterialUniform { |
||||
lowp vec4 backgroundColor; |
||||
lowp vec4 color; |
||||
highp vec4 widthSmoothnessMiterLimitReserved; |
||||
#define material_width widthSmoothnessMiterLimitReserved.x |
||||
#define material_smoothness widthSmoothnessMiterLimitReserved.y |
||||
#define material_miterLimit widthSmoothnessMiterLimitReserved.z |
||||
}; |
||||
|
||||
layout(std140 |
||||
#ifdef EXPLICIT_BINDING |
||||
, binding = 3 |
||||
#endif |
||||
) uniform Material { |
||||
MaterialUniform materials[MATERIAL_COUNT]; |
||||
}; |
||||
#endif |
||||
|
||||
/* Inputs */ |
||||
|
||||
#ifdef CAN_USE_NOPERSPECTIVE |
||||
noperspective |
||||
#endif |
||||
in highp vec2 centerDistanceSigned; |
||||
in highp float halfSegmentLength; |
||||
#ifdef CAN_USE_NOPERSPECTIVE |
||||
noperspective |
||||
#endif |
||||
in highp float hasCap; |
||||
|
||||
#ifdef VERTEX_COLOR |
||||
in lowp vec4 interpolatedVertexColor; |
||||
#endif |
||||
|
||||
#ifdef INSTANCED_OBJECT_ID |
||||
flat in highp uint interpolatedInstanceObjectId; |
||||
#endif |
||||
|
||||
#ifdef MULTI_DRAW |
||||
flat in highp uint drawId; |
||||
#endif |
||||
|
||||
/* Outputs */ |
||||
|
||||
#ifdef NEW_GLSL |
||||
#ifdef EXPLICIT_ATTRIB_LOCATION |
||||
layout(location = COLOR_OUTPUT_ATTRIBUTE_LOCATION) |
||||
#endif |
||||
out lowp vec4 fragmentColor; |
||||
#endif |
||||
#ifdef OBJECT_ID |
||||
#ifdef EXPLICIT_ATTRIB_LOCATION |
||||
layout(location = OBJECT_ID_OUTPUT_ATTRIBUTE_LOCATION) |
||||
#endif |
||||
/* mediump is just 2^10, which might not be enough, this is 2^16 */ |
||||
out highp uint fragmentObjectId; |
||||
#endif |
||||
|
||||
void main() { |
||||
#ifdef UNIFORM_BUFFERS |
||||
#ifdef OBJECT_ID |
||||
highp const uint objectId = draws[drawId].draw_objectId; |
||||
#endif |
||||
#if MATERIAL_COUNT > 1 |
||||
mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; |
||||
#else |
||||
#define materialId 0u |
||||
#endif |
||||
lowp const vec4 backgroundColor = materials[materialId].backgroundColor; |
||||
lowp const vec4 color = materials[materialId].color; |
||||
mediump const float width = materials[materialId].material_width; |
||||
mediump const float smoothness = materials[materialId].material_smoothness; |
||||
#endif |
||||
|
||||
/* Calculate a distance from the original line endpoint (B). Assuming a cap |
||||
that's not a butt, actual quad vertices (2, 3 on the left diagram) would |
||||
be at a distance `width/2` in both X and Y (in the space of the line |
||||
segment, where X is in direction of the segment and Y is in direction to |
||||
the line edges): |
||||
|
||||
----------2 --------2 |
||||
| | |
||||
[0,0] B | [0,0] B |
||||
| | |
||||
----------3 --------3 |
||||
|
||||
For a butt cap, the endpoint B would be at the edge instead (right |
||||
diagram) -- to have handling consistent for all cap styles, add |
||||
`width/2` to the center distance in that case. For fragments on the left |
||||
of B the X distance would be negative, make it 0 in that case |
||||
instead. */ |
||||
highp const vec2 centerDistance = abs(centerDistanceSigned); |
||||
highp vec2 endpointDistance = vec2(max(centerDistance.x |
||||
#ifdef CAP_STYLE_BUTT |
||||
+ width*0.5 |
||||
#endif |
||||
- halfSegmentLength, 0.0), centerDistance.y); |
||||
|
||||
/* If hasCap is negative, it means the nearest endpoint is a join, not a |
||||
cap. Thus no smoothing happens in the direction of a cap, i.e. same as |
||||
if we'd be at the center of the line. */ |
||||
if(hasCap < 0.0) endpointDistance.x = 0.0; |
||||
|
||||
/* Calculate a single distance factor out of the two-dimensional endpoint |
||||
distance. This will form the cap shape. */ |
||||
#if defined(CAP_STYLE_BUTT) || defined(CAP_STYLE_SQUARE) |
||||
highp const float distance1D = max(endpointDistance.x, endpointDistance.y); |
||||
#elif defined(CAP_STYLE_ROUND) |
||||
highp const float distance1D = length(endpointDistance); |
||||
#elif defined(CAP_STYLE_TRIANGLE) |
||||
highp const float distance1D = endpointDistance.x + endpointDistance.y; |
||||
#else |
||||
#error |
||||
#endif |
||||
|
||||
mediump const float factor = smoothstep(width*0.5 - smoothness, width*0.5 + smoothness, distance1D); |
||||
|
||||
fragmentColor = mix( |
||||
#ifdef VERTEX_COLOR |
||||
interpolatedVertexColor* |
||||
#endif |
||||
color, backgroundColor, factor); |
||||
|
||||
#ifdef OBJECT_ID |
||||
fragmentObjectId = |
||||
#ifdef INSTANCED_OBJECT_ID |
||||
interpolatedInstanceObjectId + |
||||
#endif |
||||
objectId; |
||||
#endif |
||||
} |
||||
@ -0,0 +1,472 @@
|
||||
#ifndef Magnum_Shaders_Line_h |
||||
#define Magnum_Shaders_Line_h |
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
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. |
||||
*/ |
||||
|
||||
/** @file
|
||||
* @brief Struct @ref Magnum::Shaders::LineDrawUniform, @ref Magnum::Shaders::LineMaterialUniform, enum @ref Magnum::Shaders::LineCapStyle, @ref Magnum::Shaders::LineJoinStyle, @ref Magnum::Shaders::LineVertexAnnotation, enum set @ref Magnum::Shaders::LineVertexAnnotations |
||||
* @m_since_latest |
||||
*/ |
||||
|
||||
#include <Corrade/Containers/EnumSet.h> |
||||
|
||||
#include "Magnum/Magnum.h" |
||||
#include "Magnum/Math/Color.h" |
||||
#include "Magnum/Shaders/visibility.h" |
||||
|
||||
namespace Magnum { namespace Shaders { |
||||
|
||||
/**
|
||||
@brief Line cap style |
||||
@m_since_latest |
||||
|
||||
@see @ref LineGL::capStyle(), @ref LineGL::Configuration::setCapStyle() |
||||
*/ |
||||
enum class LineCapStyle: UnsignedByte { |
||||
/**
|
||||
* [Butt cap](https://en.wikipedia.org/wiki/Butt_joint). The line is cut
|
||||
* off right at the endpoint. Lines of zero length will be invisible. |
||||
* |
||||
* @htmlinclude line-cap-butt.svg |
||||
*/ |
||||
Butt, |
||||
|
||||
/**
|
||||
* Square cap. The line is extended by half of its width past the endpoint. |
||||
* Lines of zero length will be shown as squares. |
||||
* |
||||
* @htmlinclude line-cap-square.svg |
||||
*/ |
||||
Square, |
||||
|
||||
/**
|
||||
* Round cap. The line is extended by half of its width past the endpoint. |
||||
* It's still rendered as a quad but pixels outside of the half-circle have |
||||
* the background color. Lines of zero length will be shown as circles. |
||||
* |
||||
* @htmlinclude line-cap-round.svg |
||||
* |
||||
* @see @ref LineMaterialUniform::backgroundColor, |
||||
* @ref LineGL::setBackgroundColor() |
||||
*/ |
||||
Round, |
||||
|
||||
/**
|
||||
* Triangle cap. The line is extended by half of its width past the |
||||
* endpoint. It's still rendered as a quad but pixels outside of the |
||||
* triangle have the background color. Lines of zero length will be shown |
||||
* as squares rotated by 45°. |
||||
* |
||||
* @htmlinclude line-cap-triangle.svg |
||||
* |
||||
* @see @ref LineMaterialUniform::backgroundColor, |
||||
* @ref LineGL::setBackgroundColor() |
||||
*/ |
||||
Triangle |
||||
}; |
||||
|
||||
/**
|
||||
@brief Line join style |
||||
@m_since_latest |
||||
|
||||
@see @ref LineGL::joinStyle(), @ref LineGL::Configuration::setJoinStyle() |
||||
*/ |
||||
enum class LineJoinStyle: UnsignedByte { |
||||
/**
|
||||
* [Miter join](https://en.wikipedia.org/wiki/Miter_joint). The outer edges
|
||||
* of both line segments extend until they intersect. |
||||
* |
||||
* @htmlinclude line-join-miter.svg |
||||
* |
||||
* In this style, the points `A`, `B` and `C` collapse to a zero-area |
||||
* triangle. If the miter length `l` would be larger than the limit set in |
||||
* @ref LineGL::setMiterLengthLimit() / |
||||
* @ref LineMaterialUniform::setMiterLengthLimit() or the angle between the |
||||
* two segments `α` would be less than the limit set in |
||||
* @ref LineGL::setMiterAngleLimit() / |
||||
* @ref LineMaterialUniform::setMiterAngleLimit(), it switches to |
||||
* @ref LineJoinStyle::Bevel instead. |
||||
*/ |
||||
Miter, |
||||
|
||||
/**
|
||||
* [Bevel join](https://en.wikipedia.org/wiki/Bevel). Outer edges of both
|
||||
* line segments are cut off at a right angle at their endpoints. |
||||
* |
||||
* @htmlinclude line-join-bevel.svg |
||||
* |
||||
* The area between points `A`, `B` and `C` is filled with an extra |
||||
* triangle. |
||||
*/ |
||||
Bevel |
||||
}; |
||||
|
||||
/**
|
||||
@brief Line vertex annotation |
||||
@m_since_latest |
||||
|
||||
A line segment drawn by the @ref LineGL shader consists of four vertices, first |
||||
two having the @ref LineGL::Position attribute set to the first point of the |
||||
segment and second two having it set to the second point of the segment. In |
||||
order to distinguish the direction in which the point should be expanded to |
||||
form a quad and whether the expansion should be for a line join or line cap, |
||||
each vertex contains @ref LineVertexAnnotations in the @ref LineGL::Annotation |
||||
attribute. |
||||
|
||||
@htmlinclude line-annotation.svg |
||||
|
||||
In the above diagram, there's a line strip consisting of three line segments |
||||
and six pairs of points, with @m_span{m-label m-success} green @m_endspan and |
||||
@m_span{m-label m-default} white @m_endspan forming (square) line caps, while |
||||
@m_span{m-label m-primary} azure @m_endspan, |
||||
@m_span{m-label m-danger} red @m_endspan form a miter line join, and |
||||
@m_span{m-label m-info} blue @m_endspan and |
||||
@m_span{m-label m-warning} yellow @m_endspan form a bevel join. The |
||||
twelve corresponding annotations, forming three quads (and one extra triangle |
||||
for the bevel), are shown with `U`, `J` and `B` letters, color-coded to show |
||||
which original line point they correspond to. Line cap style and join style |
||||
isn't a part of the annotation, it's set with @ref LineCapStyle and |
||||
@ref LineJoinStyle at shader compilation time instead. |
||||
|
||||
The type is 32-bit in order to match the default type of the |
||||
@ref LineGL::Annotation attribute, but the values are guaranteed to fit into 8 |
||||
bits. |
||||
*/ |
||||
enum class LineVertexAnnotation: UnsignedInt { |
||||
/**
|
||||
* The point extends upwards assuming a left-to-right direction of the line |
||||
* segment. If not set, it extends downwards. Visualized as `U` in the |
||||
* above diagram. |
||||
*/ |
||||
Up = 1 << 0, |
||||
|
||||
/**
|
||||
* The point is forming a join with a neighboring line segment defined by |
||||
* either @ref LineGL::PreviousPosition or @ref LineGL::NextPosition based |
||||
* on whether @ref LineVertexAnnotation::Begin is set. If not set, the |
||||
* point is forming a line cap, extending in the opposite of the line |
||||
* segment direction if @ref LineVertexAnnotation::Begin is set, and in the |
||||
* direction if not set. Visualized as `J` in the above diagram. |
||||
*/ |
||||
Join = 1 << 1, |
||||
|
||||
/**
|
||||
* The point is forming the beginning of the line segment, i.e. |
||||
* @ref LineGL::NextPosition contains the other point of the line segment. |
||||
* If not set, @ref LineGL::PreviousPosition contains the other point of |
||||
* the line segment instead. |
||||
* |
||||
* If @ref LineVertexAnnotation::Join is set as well, the point is a common |
||||
* point of two neighboring line segments and @ref LineGL::PreviousPosition |
||||
* contains the other point of the neighboring line segment. If |
||||
* @ref LineVertexAnnotation::Join is set and this bit is not set, |
||||
* @ref LineGL::NextPosition contaons the other point of the neighboring |
||||
* line segment instead. Visualized as `B` in the above diagram. |
||||
*/ |
||||
Begin = 1 << 2, |
||||
}; |
||||
|
||||
/**
|
||||
@brief Line vertex annotations |
||||
@m_since_latest |
||||
|
||||
Contents of the @ref LineGL::Annotation attribute. See |
||||
@ref LineVertexAnnotation for more information. |
||||
*/ |
||||
typedef Containers::EnumSet<LineVertexAnnotation> LineVertexAnnotations; |
||||
|
||||
CORRADE_ENUMSET_OPERATORS(LineVertexAnnotations) |
||||
|
||||
/**
|
||||
* @debugoperatorenum{LineCapStyle} |
||||
* @m_since_latest |
||||
*/ |
||||
MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, LineCapStyle value); |
||||
|
||||
/**
|
||||
* @debugoperatorenum{LineJoinStyle} |
||||
* @m_since_latest |
||||
*/ |
||||
MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, LineJoinStyle value); |
||||
|
||||
/**
|
||||
@brief Per-draw uniform for line shaders |
||||
@m_since_latest |
||||
|
||||
Together with the generic @ref TransformationProjectionUniform2D / |
||||
@ref TransformationProjectionUniform3D contains parameters that are specific to |
||||
each draw call. Material-related properties are expected to be shared among multiple draw calls and thus are provided in a separate |
||||
@ref LineMaterialUniform structure, referenced by @ref materialId. |
||||
@see @ref LineGL::bindDrawBuffer() |
||||
*/ |
||||
struct LineDrawUniform { |
||||
/** @brief Construct with default parameters */ |
||||
constexpr explicit LineDrawUniform(DefaultInitT = DefaultInit) noexcept: materialId{0}, objectId{0} {} |
||||
|
||||
/** @brief Construct without initializing the contents */ |
||||
explicit LineDrawUniform(NoInitT) noexcept {} |
||||
|
||||
/** @{
|
||||
* @name Convenience setters |
||||
* |
||||
* Provided to allow the use of method chaining for populating a structure |
||||
* in a single expression, otherwise equivalent to accessing the fields |
||||
* directly. Also guaranteed to provide backwards compatibility when |
||||
* packing of the actual fields changes. |
||||
*/ |
||||
|
||||
/**
|
||||
* @brief Set the @ref materialId field |
||||
* @return Reference to self (for method chaining) |
||||
*/ |
||||
LineDrawUniform& setMaterialId(UnsignedInt id) { |
||||
materialId = id; |
||||
return *this; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Set the @ref objectId field |
||||
* @return Reference to self (for method chaining) |
||||
*/ |
||||
LineDrawUniform& setObjectId(UnsignedInt id) { |
||||
objectId = id; |
||||
return *this; |
||||
} |
||||
|
||||
/**
|
||||
* @} |
||||
*/ |
||||
|
||||
/** @var materialId
|
||||
* @brief Material ID |
||||
* |
||||
* References a particular material from a @ref LineMaterialUniform array. |
||||
* Useful when an UBO with more than one material is supplied or in a |
||||
* multi-draw scenario. Should be less than the material count passed to |
||||
* @ref LineGL::Configuration::setMaterialCount(), if material count is |
||||
* @cpp 1 @ce, this field is assumed to be @cpp 0 @ce and isn't even read |
||||
* by the shader. Default value is @cpp 0 @ce, meaning the first material |
||||
* gets used. |
||||
*/ |
||||
|
||||
/* This field is an UnsignedInt in the shader and materialId is extracted
|
||||
as (value & 0xffff), so the order has to be different on BE */ |
||||
#ifndef CORRADE_TARGET_BIG_ENDIAN |
||||
alignas(4) UnsignedShort materialId; |
||||
/* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK
|
||||
I MADE THOSE UNNAMED, YOU DUMB FOOL */ |
||||
#ifndef DOXYGEN_GENERATING_OUTPUT |
||||
UnsignedShort:16; /* reserved */ |
||||
#endif |
||||
#else |
||||
alignas(4) UnsignedShort:16; /* reserved */ |
||||
UnsignedShort materialId; |
||||
#endif |
||||
|
||||
/**
|
||||
* @brief Object ID |
||||
* |
||||
* Used only for the object ID framebuffer output, not to access any other |
||||
* uniform data. Default value is @cpp 0 @ce. |
||||
* |
||||
* Used only if @ref LineGL::Flag::ObjectId is enabled, ignored otherwise. |
||||
* If @ref LineGL::Flag::InstancedObjectId is enabled as well, this value |
||||
* is added to the ID coming from the @ref LineGL::ObjectId attribute. |
||||
* @see @ref LineGL::setObjectId() |
||||
*/ |
||||
UnsignedInt objectId; |
||||
|
||||
/* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK
|
||||
I MADE THOSE UNNAMED, YOU DUMB FOOL */ |
||||
#ifndef DOXYGEN_GENERATING_OUTPUT |
||||
Int:32; |
||||
Int:32; |
||||
#endif |
||||
}; |
||||
|
||||
/**
|
||||
@brief Material uniform for line shaders |
||||
@m_since_latest |
||||
|
||||
Describes material properties referenced from |
||||
@ref LineDrawUniform::materialId. |
||||
@see @ref LineGL::bindMaterialBuffer() |
||||
*/ |
||||
struct MAGNUM_SHADERS_EXPORT LineMaterialUniform { |
||||
/** @brief Construct with default parameters */ |
||||
constexpr explicit LineMaterialUniform(DefaultInitT = DefaultInit) noexcept: backgroundColor{0.0f, 0.0f, 0.0f, 0.0f}, color{1.0f, 1.0f, 1.0f, 1.0f}, width{1.0f}, smoothness{0.0f}, miterLimit{0.875f} {} |
||||
|
||||
/** @brief Construct without initializing the contents */ |
||||
explicit LineMaterialUniform(NoInitT) noexcept: color{NoInit} {} |
||||
|
||||
/** @{
|
||||
* @name Convenience setters |
||||
* |
||||
* Provided to allow the use of method chaining for populating a structure |
||||
* in a single expression, otherwise equivalent to accessing the fields |
||||
* directly. Also guaranteed to provide backwards compatibility when |
||||
* packing of the actual fields changes. |
||||
*/ |
||||
|
||||
/**
|
||||
* @brief Set the @ref color field |
||||
* @return Reference to self (for method chaining) |
||||
*/ |
||||
LineMaterialUniform& setColor(const Color4& color) { |
||||
this->color = color; |
||||
return *this; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Set the @ref backgroundColor field |
||||
* @return Reference to self (for method chaining) |
||||
*/ |
||||
LineMaterialUniform& setBackgroundColor(const Color4& color) { |
||||
backgroundColor = color; |
||||
return *this; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Set the @ref width field |
||||
* @return Reference to self (for method chaining) |
||||
*/ |
||||
LineMaterialUniform& setWidth(Float width) { |
||||
this->width = width; |
||||
return *this; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Set the @ref smoothness field |
||||
* @return Reference to self (for method chaining) |
||||
*/ |
||||
LineMaterialUniform& setSmoothness(Float smoothness) { |
||||
this->smoothness = smoothness; |
||||
return *this; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Set the @ref miterLimit field to a length value |
||||
* @return Reference to self (for method chaining) |
||||
* |
||||
* Expects that @p limit is greater than or equal to @cpp 1.0f @ce and |
||||
* finite. |
||||
*/ |
||||
LineMaterialUniform& setMiterLengthLimit(Float limit); |
||||
|
||||
/**
|
||||
* @brief Set the @ref miterLimit field to an angle value |
||||
* @return Reference to self (for method chaining) |
||||
* |
||||
* Expects that @p limit is greater than @cpp 0.0_radf @ce. |
||||
*/ |
||||
LineMaterialUniform& setMiterAngleLimit(Rad limit); |
||||
|
||||
/**
|
||||
* @} |
||||
*/ |
||||
|
||||
/**
|
||||
* @brief Background color |
||||
* |
||||
* Default 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. |
||||
* @see @ref LineGL::setBackgroundColor(), @ref LineGL::setSmoothness(), |
||||
* @ref LineGL::Configuration::setCapStyle() |
||||
*/ |
||||
Color4 backgroundColor; |
||||
|
||||
/**
|
||||
* @brief Color |
||||
* |
||||
* Default value is @cpp 0xffffffff_rgbaf @ce. |
||||
* |
||||
* If @ref LineGL::Flag::VertexColor is enabled, the color is multiplied |
||||
* with a color coming from the @ref LineGL::Color3 / @ref LineGL::Color4 |
||||
* attribute. |
||||
* @see @ref LineGL::setColor() |
||||
*/ |
||||
Color4 color; |
||||
|
||||
/**
|
||||
* @brief Line width |
||||
* |
||||
* Screen-space, interpreted depending on the viewport size --- i.e., a |
||||
* value of @cpp 1.0f @ce is one pixel only if @ref LineGL::setViewportSize() |
||||
* is called with the actual pixel size of the viewport. Default value is |
||||
* @cpp 1.0f @ce. |
||||
* @see @ref LineGL::setWidth() |
||||
*/ |
||||
Float width; |
||||
|
||||
/**
|
||||
* @brief Line smoothness |
||||
* |
||||
* 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 LineGL::setViewportSize() is |
||||
* called with the actual pixel size of the viewport. Initial value is |
||||
* @cpp 0.0f @ce. |
||||
* @see @ref LineGL::setSmoothness() |
||||
*/ |
||||
Float smoothness; |
||||
|
||||
/**
|
||||
* @brief Miter limit |
||||
* |
||||
* Limit at which a @ref LineJoinStyle::Miter join is converted to a |
||||
* @ref LineJoinStyle::Bevel in order to avoid sharp corners extending too |
||||
* much. If joint style is not @ref LineJoinStyle::Miter, this value is |
||||
* unused. |
||||
* |
||||
* Represented as a cosine of the angle between two neighboring line |
||||
* segments, with @ref LineJoinStyle::Bevel used for angles below the limit |
||||
* (thus their cosine larger than this value). For length-based limits, |
||||
* the relation between angle @f$ \theta @f$, miter length @f$ l @f$ and |
||||
* line half-width @f$ w @f$ is as follows: @f[ |
||||
* \frac{w}{l} = \sin(\frac{\theta}{2}) |
||||
* @f] |
||||
* |
||||
* For convenience it's recommended to use the @ref setMiterLengthLimit() |
||||
* and @ref setMiterAngleLimit() helpers instead of setting this value |
||||
* directly. |
||||
* @see @ref LineGL::setMiterLengthLimit(), |
||||
* @ref LineGL::setMiterAngleLimit() |
||||
*/ |
||||
Float miterLimit; |
||||
|
||||
/* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK
|
||||
I MADE THOSE UNNAMED, YOU DUMB FOOL */ |
||||
#ifndef DOXYGEN_GENERATING_OUTPUT |
||||
Int:32; /* reserved for dynamic cap/join style */ |
||||
#endif |
||||
}; |
||||
|
||||
}} |
||||
|
||||
#endif |
||||
@ -0,0 +1,666 @@
|
||||
/* |
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
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. |
||||
*/ |
||||
|
||||
#if !defined(GL_ES) && !defined(NEW_GLSL) |
||||
#extension GL_EXT_gpu_shader4: require |
||||
#endif |
||||
|
||||
#if defined(UNIFORM_BUFFERS) && defined(TEXTURE_ARRAYS) && !defined(GL_ES) |
||||
#extension GL_ARB_shader_bit_encoding: require |
||||
#endif |
||||
|
||||
/* Use the noperspective keyword to avoid artifacts in screen-space |
||||
interpolation if perspective projection is used in 3D. If not available, |
||||
it's worked around by dividing gl_Position with gl_Position.w (which is |
||||
extra instructions, so the noperspective keyword is preferred). */ |
||||
#ifndef GL_ES |
||||
#define CAN_USE_NOPERSPECTIVE |
||||
#elif defined(GL_ES) && defined(GL_NV_shader_noperspective_interpolation) |
||||
#extension GL_NV_shader_noperspective_interpolation: require |
||||
#define CAN_USE_NOPERSPECTIVE |
||||
#endif |
||||
|
||||
#ifdef MULTI_DRAW |
||||
#ifndef GL_ES |
||||
#extension GL_ARB_shader_draw_parameters: require |
||||
#else /* covers WebGL as well */ |
||||
#extension GL_ANGLE_multi_draw: require |
||||
#endif |
||||
#endif |
||||
|
||||
#ifndef NEW_GLSL |
||||
#define in attribute |
||||
#define out varying |
||||
#endif |
||||
|
||||
#ifndef RUNTIME_CONST |
||||
#define const |
||||
#endif |
||||
|
||||
/* Uniforms */ |
||||
|
||||
/* This one is for both classic and UBOs, as it's usually set globally instead |
||||
of changing per-draw */ |
||||
#ifdef EXPLICIT_UNIFORM_LOCATION |
||||
layout(location = 0) |
||||
#endif |
||||
uniform lowp vec2 viewportSize; /* defaults to zero */ |
||||
|
||||
#ifndef UNIFORM_BUFFERS |
||||
#ifdef EXPLICIT_UNIFORM_LOCATION |
||||
layout(location = 1) |
||||
#endif |
||||
#ifdef TWO_DIMENSIONS |
||||
uniform highp mat3 transformationProjectionMatrix |
||||
#ifndef GL_ES |
||||
= mat3(1.0) |
||||
#endif |
||||
; |
||||
#elif defined(THREE_DIMENSIONS) |
||||
uniform highp mat4 transformationProjectionMatrix |
||||
#ifndef GL_ES |
||||
= mat4(1.0) |
||||
#endif |
||||
; |
||||
#else |
||||
#error |
||||
#endif |
||||
|
||||
#ifdef EXPLICIT_UNIFORM_LOCATION |
||||
layout(location = 4) |
||||
#endif |
||||
uniform mediump float width |
||||
#ifndef GL_ES |
||||
= 1.0 |
||||
#endif |
||||
; |
||||
|
||||
#ifdef EXPLICIT_UNIFORM_LOCATION |
||||
layout(location = 5) |
||||
#endif |
||||
uniform mediump float smoothness |
||||
#ifndef GL_ES |
||||
= 0.0 |
||||
#endif |
||||
; |
||||
|
||||
#ifdef EXPLICIT_UNIFORM_LOCATION |
||||
layout(location = 6) |
||||
#endif |
||||
uniform mediump float miterLimit |
||||
#ifndef GL_ES |
||||
/* cos(2*asin(1.0/4.0)), with 4 being the documented length limit */ |
||||
= 0.875 |
||||
#endif |
||||
; |
||||
|
||||
/* Uniform buffers */ |
||||
|
||||
#else |
||||
#if DRAW_COUNT > 1 |
||||
#ifdef EXPLICIT_UNIFORM_LOCATION |
||||
layout(location = 1) |
||||
#endif |
||||
uniform highp uint drawOffset |
||||
#ifndef GL_ES |
||||
= 0u |
||||
#endif |
||||
; |
||||
#else |
||||
#define drawOffset 0u |
||||
#endif |
||||
|
||||
layout(std140 |
||||
#ifdef EXPLICIT_BINDING |
||||
, binding = 1 |
||||
#endif |
||||
) uniform TransformationProjection { |
||||
highp |
||||
#ifdef TWO_DIMENSIONS |
||||
/* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for |
||||
details */ |
||||
mat3x4 |
||||
#elif defined(THREE_DIMENSIONS) |
||||
mat4 |
||||
#else |
||||
#error |
||||
#endif |
||||
transformationProjectionMatrices[DRAW_COUNT]; |
||||
}; |
||||
|
||||
struct DrawUniform { |
||||
highp uvec4 materialIdReservedObjectIdReservedReserved; |
||||
#define draw_materialIdReserved materialIdReservedObjectIdReservedReserved.x |
||||
#define draw_objectId materialIdReservedObjectIdReservedReserved.y |
||||
}; |
||||
|
||||
layout(std140 |
||||
#ifdef EXPLICIT_BINDING |
||||
, binding = 2 |
||||
#endif |
||||
) uniform Draw { |
||||
DrawUniform draws[DRAW_COUNT]; |
||||
}; |
||||
|
||||
struct MaterialUniform { |
||||
lowp vec4 backgroundColor; |
||||
lowp vec4 color; |
||||
highp vec4 widthSmoothnessMiterLimitReserved; |
||||
#define material_width widthSmoothnessMiterLimitReserved.x |
||||
#define material_smoothness widthSmoothnessMiterLimitReserved.y |
||||
#define material_miterLimit widthSmoothnessMiterLimitReserved.z |
||||
}; |
||||
|
||||
layout(std140 |
||||
#ifdef EXPLICIT_BINDING |
||||
, binding = 3 |
||||
#endif |
||||
) uniform Material { |
||||
MaterialUniform materials[MATERIAL_COUNT]; |
||||
}; |
||||
#endif |
||||
|
||||
/* Inputs */ |
||||
|
||||
#ifdef EXPLICIT_ATTRIB_LOCATION |
||||
layout(location = POSITION_ATTRIBUTE_LOCATION) |
||||
#endif |
||||
#ifdef TWO_DIMENSIONS |
||||
in highp vec2 position; |
||||
#elif defined(THREE_DIMENSIONS) |
||||
/* Last component is reserved for line distance */ |
||||
in highp vec3 position; |
||||
#else |
||||
#error |
||||
#endif |
||||
|
||||
#ifdef EXPLICIT_ATTRIB_LOCATION |
||||
layout(location = LINE_PREVIOUS_POSITION_ATTRIBUTE_LOCATION) |
||||
#endif |
||||
#ifdef TWO_DIMENSIONS |
||||
in highp vec2 previousPosition; |
||||
#elif defined(THREE_DIMENSIONS) |
||||
in highp vec3 previousPosition; |
||||
#else |
||||
#error |
||||
#endif |
||||
|
||||
#ifdef EXPLICIT_ATTRIB_LOCATION |
||||
layout(location = LINE_NEXT_POSITION_ATTRIBUTE_LOCATION) |
||||
#endif |
||||
#ifdef TWO_DIMENSIONS |
||||
in highp vec2 nextPosition; |
||||
#elif defined(THREE_DIMENSIONS) |
||||
in highp vec3 nextPosition; |
||||
#else |
||||
#error |
||||
#endif |
||||
|
||||
/* Point annotation, matching the LineVertexAnnotation enum bits */ |
||||
#define ANNOTATION_UP_MASK 1u |
||||
#define ANNOTATION_JOIN_MASK 2u |
||||
#define ANNOTATION_BEGIN_MASK 4u |
||||
#ifdef EXPLICIT_ATTRIB_LOCATION |
||||
layout(location = LINE_ANNOTATION_ATTRIBUTE_LOCATION) |
||||
#endif |
||||
in lowp uint annotation; |
||||
|
||||
#ifdef VERTEX_COLOR |
||||
#ifdef EXPLICIT_ATTRIB_LOCATION |
||||
layout(location = COLOR_ATTRIBUTE_LOCATION) |
||||
#endif |
||||
in lowp vec4 vertexColor; |
||||
#endif |
||||
|
||||
#ifdef INSTANCED_OBJECT_ID |
||||
#ifdef EXPLICIT_ATTRIB_LOCATION |
||||
layout(location = OBJECT_ID_ATTRIBUTE_LOCATION) |
||||
#endif |
||||
in highp uint instanceObjectId; |
||||
#endif |
||||
|
||||
#ifdef INSTANCED_TRANSFORMATION |
||||
#ifdef EXPLICIT_ATTRIB_LOCATION |
||||
layout(location = TRANSFORMATION_MATRIX_ATTRIBUTE_LOCATION) |
||||
#endif |
||||
#ifdef TWO_DIMENSIONS |
||||
in highp mat3 instancedTransformationMatrix; |
||||
#elif defined(THREE_DIMENSIONS) |
||||
in highp mat4 instancedTransformationMatrix; |
||||
#else |
||||
#error |
||||
#endif |
||||
#endif |
||||
|
||||
/* Outputs */ |
||||
|
||||
#ifdef CAN_USE_NOPERSPECTIVE |
||||
noperspective |
||||
#endif |
||||
out highp vec2 centerDistanceSigned; |
||||
out highp float halfSegmentLength; |
||||
#ifdef CAN_USE_NOPERSPECTIVE |
||||
noperspective |
||||
#endif |
||||
out highp float hasCap; |
||||
|
||||
#ifdef VERTEX_COLOR |
||||
out lowp vec4 interpolatedVertexColor; |
||||
#endif |
||||
|
||||
#ifdef INSTANCED_OBJECT_ID |
||||
flat out highp uint interpolatedInstanceObjectId; |
||||
#endif |
||||
|
||||
#ifdef MULTI_DRAW |
||||
flat out highp uint drawId; |
||||
#endif |
||||
|
||||
/* Same as Math::Vector2::perpendicular() */ |
||||
vec2 perpendicular(vec2 a) { |
||||
return vec2(-a.y, a.x); |
||||
} |
||||
|
||||
void main() { |
||||
#ifdef UNIFORM_BUFFERS |
||||
#ifdef MULTI_DRAW |
||||
drawId = drawOffset + uint( |
||||
#ifndef GL_ES |
||||
gl_DrawIDARB /* Using GL_ARB_shader_draw_parameters, not GLSL 4.6 */ |
||||
#else |
||||
gl_DrawID |
||||
#endif |
||||
); |
||||
#else |
||||
#define drawId drawOffset |
||||
#endif |
||||
|
||||
#ifdef TWO_DIMENSIONS |
||||
highp const mat3 transformationProjectionMatrix = mat3(transformationProjectionMatrices[drawId]); |
||||
#elif defined(THREE_DIMENSIONS) |
||||
highp const mat4 transformationProjectionMatrix = transformationProjectionMatrices[drawId]; |
||||
#else |
||||
#error |
||||
#endif |
||||
#if MATERIAL_COUNT > 1 |
||||
mediump const uint materialId = draws[drawId].draw_materialIdReserved & 0xffffu; |
||||
#else |
||||
#define materialId 0u |
||||
#endif |
||||
mediump const float width = materials[materialId].material_width; |
||||
mediump const float smoothness = materials[materialId].material_smoothness; |
||||
highp const float miterLimit = materials[materialId].material_miterLimit; |
||||
#endif |
||||
|
||||
#ifdef TWO_DIMENSIONS |
||||
highp const vec2 transformedPosition = (transformationProjectionMatrix* |
||||
#ifdef INSTANCED_TRANSFORMATION |
||||
instancedTransformationMatrix* |
||||
#endif |
||||
vec3(position, 1.0)).xy; |
||||
|
||||
highp const vec2 transformedPreviousPosition = (transformationProjectionMatrix* |
||||
#ifdef INSTANCED_TRANSFORMATION |
||||
instancedTransformationMatrix* |
||||
#endif |
||||
vec3(previousPosition, 1.0)).xy; |
||||
|
||||
highp const vec2 transformedNextPosition = (transformationProjectionMatrix* |
||||
#ifdef INSTANCED_TRANSFORMATION |
||||
instancedTransformationMatrix* |
||||
#endif |
||||
vec3(nextPosition, 1.0)).xy; |
||||
#elif defined(THREE_DIMENSIONS) |
||||
highp const vec4 transformedPosition4 = transformationProjectionMatrix* |
||||
#ifdef INSTANCED_TRANSFORMATION |
||||
instancedTransformationMatrix* |
||||
#endif |
||||
vec4(position, 1.0); |
||||
highp const vec2 transformedPosition = transformedPosition4.xy/transformedPosition4.w; |
||||
|
||||
highp const vec4 transformedPreviousPosition4 = transformationProjectionMatrix* |
||||
#ifdef INSTANCED_TRANSFORMATION |
||||
instancedTransformationMatrix* |
||||
#endif |
||||
vec4(previousPosition, 1.0); |
||||
highp const vec2 transformedPreviousPosition = transformedPreviousPosition4.xy/transformedPreviousPosition4.w; |
||||
|
||||
highp const vec4 transformedNextPosition4 = transformationProjectionMatrix* |
||||
#ifdef INSTANCED_TRANSFORMATION |
||||
instancedTransformationMatrix* |
||||
#endif |
||||
vec4(nextPosition, 1.0); |
||||
highp const vec2 transformedNextPosition = transformedNextPosition4.xy/transformedNextPosition4.w; |
||||
#else |
||||
#error |
||||
#endif |
||||
|
||||
/* Decide about the line direction vector `d` and edge direction vector `e` |
||||
from the `pointMarkerComponent` input. Quad corners 0 and 1 come from |
||||
segment endpoint A, are marked with the POINT_MARKER_BEGIN_MASK bit and |
||||
so their line direction is taken from `nextPosition`, quad corners 2 and |
||||
3 come from B and are *not* marked with POINT_MARKER_BEGIN_MASK and so |
||||
their line direction is taken from `previousPosition`, with the |
||||
direction being always from point A to point B. The edge direction is |
||||
then perpendicular to the line direction, with points 0 and 2 marked |
||||
with POINT_MARKER_UP_MASK using it directly, while points 1 and 3 |
||||
don't have POINT_MARKER_UP_MASK and have to negate it: |
||||
|
||||
^ ^ |
||||
e e |
||||
| | |
||||
[UP, BEGIN] 0-d--> 2-d--> [UP] |
||||
|
||||
A B |
||||
|
||||
[BEGIN] 1-d--> 3-d--> [] |
||||
| | |
||||
e e |
||||
v v |
||||
|
||||
The POINT_MARKER_CAP_MASK is then used below. */ |
||||
highp const vec2 lineDirection = bool(annotation & ANNOTATION_BEGIN_MASK) ? |
||||
transformedNextPosition - transformedPosition : |
||||
transformedPosition - transformedPreviousPosition; |
||||
mediump const float edgeSign = bool(annotation & ANNOTATION_UP_MASK) ? 1.0 : -1.0; |
||||
mediump const float neighborSign = bool(annotation & ANNOTATION_BEGIN_MASK) ? -1.0 : 1.0; |
||||
|
||||
/* Line direction and its length converted from the [-1, 1] unit square to |
||||
the screen space so we properly take aspect ratio into account. In the |
||||
end it undoes the transformation by multiplying by 2.0/viewportSize |
||||
again. */ |
||||
highp const vec2 screenspaceLineDirection = lineDirection*viewportSize/2.0; |
||||
highp const float screenspaceLineDirectionLength = length(screenspaceLineDirection); |
||||
|
||||
/* Normalized screenspace line and edge direction. In case of zero-sized |
||||
lines (i.e., points) the X axis is picked as line direction instead, and |
||||
thus Y axis for edge direction. */ |
||||
highp const vec2 screenspaceLineDirectionNormalized = screenspaceLineDirectionLength == 0.0 ? vec2(1.0, 0.0) : screenspaceLineDirection/screenspaceLineDirectionLength; |
||||
highp const vec2 screenspaceEdgeDirectionNormalized = perpendicular(screenspaceLineDirectionNormalized); |
||||
|
||||
/* Line width includes also twice the smoothness (because it's a radius |
||||
instead of a diameter, and is on both sides of the line), and is rounded |
||||
to whole pixels. So for the edge distance we need half of it. */ |
||||
mediump const float edgeDistance = ceil(width + 2.0*smoothness)*0.5; |
||||
#ifdef CAP_STYLE_BUTT |
||||
mediump const float capDistance = ceil(2.0*smoothness)*0.5; |
||||
#elif defined(CAP_STYLE_SQUARE) || defined(CAP_STYLE_ROUND) || defined(CAP_STYLE_TRIANGLE) |
||||
mediump const float capDistance = edgeDistance; |
||||
#else |
||||
#error |
||||
#endif |
||||
|
||||
/* Line segment half-length, passed to the fragment shader. Same for all |
||||
four points. */ |
||||
halfSegmentLength = screenspaceLineDirectionLength*0.5; |
||||
|
||||
/* Calculate the actual endpoint parameters depending on whether we're at a |
||||
line cap, line join bevel, line join miter etc. |
||||
|
||||
- `screenspacePointDirection` contains screenspace direction from |
||||
`transformedPosition` to the actual point. After undoing the |
||||
screenspace projection the sum of the two is written to |
||||
gl_Position. |
||||
- `centerDistanceSigned` contains signed distance from the edge to |
||||
center, passed to the fragment shader. It's chosen in a way that |
||||
interpolates to zero in the quad center, and the area where |
||||
`all(abs(centerDistanceSigned) <= vec2(halfSegmentLength + |
||||
capDistance, edgeDistance))` is inside the line. |
||||
- `hasCap` contains `abs(centerDistanceSigned.x)` with a sign |
||||
positive if the point is a cap and negative if it isn't. Given |
||||
segment endpoints A and B (and quad points 0/1 and 2/3 |
||||
corresponding to these), the following cases can happen: |
||||
|
||||
- if both have a cap, it's a negative value in both, thus has a |
||||
constant negative value in the fragment shader |
||||
- if neither have a cap, it's a positive value in both, thus has |
||||
a constant positive value in the fragment shader |
||||
- if one has a cap and the other not, it's a negative value in |
||||
one and positive in the other, interpolating to zero in the |
||||
quad center |
||||
|
||||
In the fragment shader, `abs(centerDistanceSigned)` and `sign(hasCap)` |
||||
is then used to perform cap rendering and antialiasing. For example, |
||||
with a standalone line segment that has square caps on both ends, the |
||||
value of `centerDistanceSigned` is like in the following diagram, with |
||||
`d` being `halfSegmentLength`, `w` being `edgeDistance`, `c` being |
||||
`capDistance`, and an extra margin for `smoothness` indicated by `s` and |
||||
the double border: |
||||
|
||||
[-d-c-s,+w+s] [+d+c+s,+w+s] |
||||
0-----------------------------2 |
||||
[-d-c,+w]------------------[+d+c,+w] |
||||
| | | | hasCap[0] = hasCap[1] = +d+c+s |
||||
[-d-c,0] [0,0] [+d+c,0] |
||||
| | | | hasCap[2] = hasCap[3] = +d+c+s |
||||
[-d-c,-w]------------------[+d+c,-w] |
||||
1-----------------------------3 |
||||
[-d-c-s,-w-s] [+d+c+s,-w+s] |
||||
|
||||
With a cap only on the left side, `centerDistanceSigned` would be like |
||||
this. Note the absence of a smoothness margin on the right side: |
||||
|
||||
[-d-c-s,+w+s] [+d,+w+s] |
||||
0---------------------------2 |
||||
[-d-c,+w]-------------------[+d,+w] |
||||
| | | hasCap[0] = hasCap[1] = +d+c+s |
||||
[-d-c,0] [0,0] [+d,0] |
||||
| | | hasCap[2] = hasCap[3] = -d |
||||
[-d-c,-w]-------------------[+d,-w] |
||||
1---------------------------3 |
||||
[-d-c-s,-w-s] [+d,-w-s] |
||||
|
||||
*/ |
||||
centerDistanceSigned = |
||||
/* The the Y coordinate is same for all cases, X coordinate gets |
||||
further adjusted below */ |
||||
vec2(halfSegmentLength*neighborSign, edgeDistance*edgeSign); |
||||
highp vec2 screenspacePointDirection; |
||||
|
||||
/* Line join */ |
||||
if(bool(annotation & ANNOTATION_JOIN_MASK)) { |
||||
/* Neighbor direction `nd`, needed to distinguish whether this is the |
||||
inner or outer join point. Calculated with basically an inverse of |
||||
the logic used to calculate `lineDirection`, with the neighbor |
||||
direction always pointing from the A/B endpoint to the other |
||||
neighbor line endpoint: |
||||
|
||||
<--nd-0 [BEGIN] [END] 2-nd--> |
||||
|
||||
A B |
||||
|
||||
<--nd-1 [BEGIN] [END] 3-nd--> */ |
||||
highp const vec2 neighborDirection = bool(annotation & ANNOTATION_BEGIN_MASK) ? |
||||
transformedPreviousPosition - transformedPosition : |
||||
transformedNextPosition - transformedPosition; |
||||
/* Screenspace neighbor direction and its length, calculated |
||||
equivalently to screenspace line direction above */ |
||||
highp const vec2 screenspaceNeighborDirectionNormalized = normalize(neighborDirection*viewportSize/2.0); |
||||
|
||||
/* If the edge direction vector `e` and the neighbor direction vector |
||||
`nd` point to the opposite direction (i.e., their dot product is |
||||
negative), this is an outer point of the line and a candidate for |
||||
a bevel. |
||||
|
||||
^ |
||||
e |
||||
| |
||||
-d->-2 |
||||
|\ |
||||
B | nd |
||||
| \ |
||||
-----3 v |
||||
|
||||
If a miter join is used instead of a bevel, the point is beveled |
||||
only if the line direction `d` and neighbor direction `nd` is |
||||
sharper than a limit (i.e., their dot product, or a cosine of their |
||||
angle, is between `[-1, -miterLimit]`). */ |
||||
const bool outerBeveledPoint = |
||||
dot(screenspaceEdgeDirectionNormalized*edgeSign, screenspaceNeighborDirectionNormalized) < 0.0 |
||||
#if defined(JOIN_STYLE_MITER) |
||||
&& dot(screenspaceLineDirectionNormalized*neighborSign, screenspaceNeighborDirectionNormalized) < -miterLimit |
||||
#elif !defined(JOIN_STYLE_BEVEL) |
||||
#error |
||||
#endif |
||||
; |
||||
|
||||
/* Outer point of a beveled join -- although |
||||
https://www.w3.org/TR/svg-strokes/#LineJoin doesn't define *what |
||||
exactly* is a bevel, it's defined as "Cuts the outside edge off |
||||
where a circle the diameter of the stroke intersects the stroke." at |
||||
e.g. https://apike.ca/prog_svg_line_cap_join.html. |
||||
|
||||
0--- ----2a |
||||
| |^\ |
||||
| | e -_ |
||||
| | |ρ \ |
||||
A-- ----|--B-e->2b |
||||
| | | _-| |
||||
| | _- | |
||||
| | _- | | |
||||
1-- --3 | | |
||||
| | | |
||||
C |
||||
|
||||
Which ultimately means the `2a` and `2b` quad endpoints are simply |
||||
the edge direction vector `e` away from point B, in one case with |
||||
the `e` calculated from the AB segment, and in the other from the BC |
||||
segment. */ |
||||
if(outerBeveledPoint) { |
||||
screenspacePointDirection = screenspaceEdgeDirectionNormalized*edgeDistance*edgeSign; |
||||
/* centerDistanceSigned doesn't need any adjustment, hasCap is set |
||||
below for both */ |
||||
|
||||
/* Otherwise it's either an outer point of a miter join (basically |
||||
points 2a and 2b from above evaluated to the same position), or the |
||||
inner point, which is the same for bevel and mitter joins. Given |
||||
normalized direction `d` and neighbor direction `nd`, |
||||
`normalized(d + nd)` is the "average" direction of the two and `perpendicular(normalized(d + nd))` gives us the direction from B to |
||||
2 (or from 3 to B): |
||||
|
||||
0--- --------+---2 |
||||
| | α/ \ |
||||
| w | / j \ |
||||
| |/ \ |
||||
A-- +_-----d->-B \ |
||||
| -_ α/α\ \ |
||||
| -_ / nd \ |
||||
| d + nd /-_ v \ |
||||
1---- ----3 -_ \ |
||||
\ -+ |
||||
\ \ |
||||
C |
||||
|
||||
With `2α` being the angle between `d` and `nd`, `α` appears in two |
||||
right triangles and the following holds, `w` being the edge distance |
||||
from above, and `j` having the length that's needed to scale |
||||
`perpendicular(normalized(d + nd))` to get point 2: |
||||
|
||||
|d + nd| w 2 w |
||||
sin(α) = -------- = --- --> |j| = -------- |
||||
2 |d| |j| |d + nd| |
||||
|
||||
Then, vector j is the following, meaning we avoid the normalization |
||||
square root completely: |
||||
|
||||
perp(d + nd) (2 w)perp(d + nd) |
||||
j = |j| ------------ = ----------------- |
||||
|d + nd| dot(d + nd) |
||||
|
||||
Point 3 is then just in the opposite direction; for the other side |
||||
it's done equivalently. */ |
||||
} else { |
||||
highp const vec2 averageDirection = neighborSign*screenspaceLineDirectionNormalized + screenspaceNeighborDirectionNormalized; |
||||
screenspacePointDirection = (perpendicular(averageDirection)*(neighborSign*edgeSign*2.0*edgeDistance/dot(averageDirection, averageDirection))); |
||||
|
||||
/* By projecting the point direction onto the line direction we |
||||
get a signed distance from the endpoint, adjust center distance |
||||
with that */ |
||||
centerDistanceSigned.x += dot(screenspacePointDirection, screenspaceLineDirectionNormalized); |
||||
} |
||||
|
||||
/* No cap here, store a negative value. TODO If |
||||
sign(centerDistanceSigned.x) is different from neighborSign, then |
||||
the sign here should be taken based on whether the other point is a |
||||
join -- add more bits to the vertex annotation? */ |
||||
hasCap = -abs(centerDistanceSigned.x); |
||||
|
||||
/* Line cap otherwise -- the quad corner 0/1/2/3 a sum of the signed cap |
||||
distance (`cdS`) and signed edge distance vectors (`eDS`), which are |
||||
formed by the line direction vector `d` and its perpendicular vector. |
||||
Neighbor direction (i.e., the other input from the one used to calculate |
||||
`lineDirection`) isn't used at all in this case. |
||||
|
||||
cDS |
||||
0<---+---------- |
||||
| ^ |
||||
| | eDS |
||||
| | |
||||
| A--d--> |
||||
| |
||||
| |
||||
| |
||||
1--- |
||||
|
||||
The signed center distance a sum of half segment length and the cap |
||||
distance, multiplied by the cap sign (thus negative for points derived |
||||
from A and positive for B). */ |
||||
} else { |
||||
screenspacePointDirection = |
||||
screenspaceLineDirectionNormalized*capDistance*neighborSign + |
||||
screenspaceEdgeDirectionNormalized*edgeDistance*edgeSign; |
||||
|
||||
/* Add signed cap distance to the center distance */ |
||||
centerDistanceSigned.x += capDistance*neighborSign; |
||||
|
||||
/* Cap is here, store a positive value */ |
||||
hasCap = abs(centerDistanceSigned.x); |
||||
} |
||||
|
||||
/* Undo the screenspace projection */ |
||||
highp const vec2 pointDirection = screenspacePointDirection*2.0/viewportSize; |
||||
|
||||
#ifdef TWO_DIMENSIONS |
||||
gl_Position = vec4(transformedPosition + pointDirection, 0.0, 1.0); |
||||
#elif defined(THREE_DIMENSIONS) |
||||
gl_Position = vec4(transformedPosition4.xy + pointDirection*transformedPosition4.w, transformedPosition4.zw); |
||||
#ifndef CAN_USE_NOPERSPECTIVE |
||||
/* See the CAN_USE_NOPERSPECTIVE macro definition at the top for details */ |
||||
gl_Position /= gl_Position.w; |
||||
#endif |
||||
#else |
||||
#error |
||||
#endif |
||||
|
||||
#ifdef VERTEX_COLOR |
||||
/* Vertex colors, if enabled */ |
||||
interpolatedVertexColor = vertexColor; |
||||
#endif |
||||
|
||||
#ifdef INSTANCED_OBJECT_ID |
||||
/* Instanced object ID, if enabled */ |
||||
interpolatedInstanceObjectId = instanceObjectId; |
||||
#endif |
||||
} |
||||
@ -0,0 +1,432 @@
|
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
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. |
||||
*/ |
||||
|
||||
#include "LineGL.h" |
||||
|
||||
#include <Corrade/Containers/EnumSet.hpp> |
||||
#include <Corrade/Containers/Iterable.h> |
||||
#include <Corrade/Containers/String.h> |
||||
#include <Corrade/Utility/Format.h> |
||||
#include <Corrade/Utility/Resource.h> |
||||
|
||||
#include "Magnum/GL/Buffer.h" |
||||
#include "Magnum/GL/Context.h" |
||||
#include "Magnum/GL/Extensions.h" |
||||
#include "Magnum/GL/Shader.h" |
||||
#include "Magnum/Math/Color.h" |
||||
#include "Magnum/Math/Matrix3.h" |
||||
#include "Magnum/Math/Matrix4.h" |
||||
#include "Magnum/Shaders/Line.h" |
||||
#include "Magnum/Shaders/Implementation/CreateCompatibilityShader.h" |
||||
#include "Magnum/Shaders/Implementation/lineMiterLimit.h" |
||||
|
||||
namespace Magnum { namespace Shaders { |
||||
|
||||
using namespace Containers::Literals; |
||||
using namespace Math::Literals; |
||||
|
||||
namespace { |
||||
enum: Int { |
||||
/* 0/1/2/3 taken by Phong (A/D/S/N), 4 by MeshVisualizer colormap, 5 by
|
||||
object ID textures, 6 by Vector */ |
||||
TextureUnit = 7 |
||||
}; |
||||
|
||||
enum: Int { |
||||
/* Not using the zero binding to avoid conflicts with
|
||||
ProjectionBufferBinding from other shaders which can likely stay |
||||
bound to the same buffer for the whole time */ |
||||
TransformationProjectionBufferBinding = 1, |
||||
DrawBufferBinding = 2, |
||||
MaterialBufferBinding = 3 |
||||
}; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> typename LineGL<dimensions>::CompileState LineGL<dimensions>::compile(const Configuration& configuration) { |
||||
CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.materialCount(), |
||||
"Shaders::LineGL: material count can't be zero", CompileState{NoCreate}); |
||||
CORRADE_ASSERT(!(configuration.flags() >= Flag::UniformBuffers) || configuration.drawCount(), |
||||
"Shaders::LineGL: draw count can't be zero", CompileState{NoCreate}); |
||||
|
||||
#ifndef MAGNUM_TARGET_GLES |
||||
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::EXT::gpu_shader4); |
||||
if(configuration.flags() >= Flag::UniformBuffers) |
||||
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object); |
||||
#endif |
||||
if(configuration.flags() >= Flag::MultiDraw) { |
||||
#ifndef MAGNUM_TARGET_GLES |
||||
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::shader_draw_parameters); |
||||
#elif !defined(MAGNUM_TARGET_WEBGL) |
||||
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ANGLE::multi_draw); |
||||
#else |
||||
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::WEBGL::multi_draw); |
||||
#endif |
||||
} |
||||
|
||||
#ifdef MAGNUM_BUILD_STATIC |
||||
/* Import resources on static build, if not already */ |
||||
if(!Utility::Resource::hasGroup("MagnumShadersGL"_s)) |
||||
importShaderResources(); |
||||
#endif |
||||
Utility::Resource rs{"MagnumShadersGL"_s}; |
||||
|
||||
|
||||
#ifndef MAGNUM_TARGET_GLES |
||||
const GL::Context& context = GL::Context::current(); |
||||
const GL::Version version = context.supportedVersion({GL::Version::GL320, GL::Version::GL310, GL::Version::GL300, GL::Version::GL210}); |
||||
#else |
||||
constexpr GL::Version version = GL::Version::GLES300; |
||||
#endif |
||||
|
||||
/* Cap and join style is needed by both the vertex and fragment shader,
|
||||
prepare their defines just once for both */ |
||||
Containers::StringView capStyleDefine, joinStyleDefine; |
||||
switch(configuration.capStyle()) { |
||||
case LineCapStyle::Butt: |
||||
capStyleDefine = "#define CAP_STYLE_BUTT\n"_s; |
||||
break; |
||||
case LineCapStyle::Square: |
||||
capStyleDefine = "#define CAP_STYLE_SQUARE\n"_s; |
||||
break; |
||||
case LineCapStyle::Round: |
||||
capStyleDefine = "#define CAP_STYLE_ROUND\n"_s; |
||||
break; |
||||
case LineCapStyle::Triangle: |
||||
capStyleDefine = "#define CAP_STYLE_TRIANGLE\n"_s; |
||||
break; |
||||
} |
||||
switch(configuration.joinStyle()) { |
||||
case LineJoinStyle::Miter: |
||||
joinStyleDefine = "#define JOIN_STYLE_MITER\n"_s; |
||||
break; |
||||
case LineJoinStyle::Bevel: |
||||
joinStyleDefine = "#define JOIN_STYLE_BEVEL\n"_s; |
||||
break; |
||||
} |
||||
CORRADE_INTERNAL_ASSERT(capStyleDefine); |
||||
CORRADE_INTERNAL_ASSERT(joinStyleDefine); |
||||
|
||||
GL::Shader vert = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Vertex); |
||||
vert.addSource(capStyleDefine) |
||||
.addSource(joinStyleDefine) |
||||
.addSource(configuration.flags() & Flag::VertexColor ? "#define VERTEX_COLOR\n"_s : ""_s) |
||||
.addSource(dimensions == 2 ? "#define TWO_DIMENSIONS\n"_s : "#define THREE_DIMENSIONS\n"_s) |
||||
.addSource(configuration.flags() >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n"_s : ""_s) |
||||
.addSource(configuration.flags() & Flag::InstancedTransformation ? "#define INSTANCED_TRANSFORMATION\n"_s : ""_s); |
||||
if(configuration.flags() >= Flag::UniformBuffers) { |
||||
vert.addSource(Utility::format( |
||||
"#define UNIFORM_BUFFERS\n" |
||||
"#define DRAW_COUNT {}\n" |
||||
"#define MATERIAL_COUNT {}\n", |
||||
configuration.drawCount(), |
||||
configuration.materialCount())); |
||||
vert.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); |
||||
} |
||||
vert.addSource(rs.getString("generic.glsl"_s)) |
||||
.addSource(rs.getString("Line.vert"_s)) |
||||
.submitCompile(); |
||||
|
||||
GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment); |
||||
frag.addSource(capStyleDefine) |
||||
.addSource(joinStyleDefine) |
||||
.addSource(configuration.flags() & Flag::VertexColor ? "#define VERTEX_COLOR\n"_s : ""_s) |
||||
.addSource(configuration.flags() & Flag::ObjectId ? "#define OBJECT_ID\n"_s : ""_s) |
||||
.addSource(configuration.flags() >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n"_s : ""_s); |
||||
if(configuration.flags() >= Flag::UniformBuffers) { |
||||
frag.addSource(Utility::format( |
||||
"#define UNIFORM_BUFFERS\n" |
||||
"#define DRAW_COUNT {}\n" |
||||
"#define MATERIAL_COUNT {}\n", |
||||
configuration.drawCount(), |
||||
configuration.materialCount())); |
||||
frag.addSource(configuration.flags() >= Flag::MultiDraw ? "#define MULTI_DRAW\n"_s : ""_s); |
||||
} |
||||
frag.addSource(rs.getString("generic.glsl"_s)) |
||||
.addSource(rs.getString("Line.frag"_s)) |
||||
.submitCompile(); |
||||
|
||||
LineGL<dimensions> out{NoInit}; |
||||
out._flags = configuration.flags(); |
||||
out._capStyle = configuration.capStyle(); |
||||
out._joinStyle = configuration.joinStyle(); |
||||
out._materialCount = configuration.materialCount(); |
||||
out._drawCount = configuration.drawCount(); |
||||
|
||||
out.attachShaders({vert, frag}); |
||||
|
||||
/* ES3 has this done in the shader directly and doesn't even provide
|
||||
bindFragmentDataLocation() */ |
||||
#ifndef MAGNUM_TARGET_GLES |
||||
if(!context.isExtensionSupported<GL::Extensions::ARB::explicit_attrib_location>(version)) { |
||||
out.bindAttributeLocation(Position::Location, "position"_s); |
||||
out.bindAttributeLocation(PreviousPosition::Location, "previousPosition"_s); |
||||
out.bindAttributeLocation(NextPosition::Location, "nextPosition"_s); |
||||
out.bindAttributeLocation(Annotation::Location, "annotation"_s); |
||||
if(configuration.flags() & Flag::VertexColor) |
||||
out.bindAttributeLocation(Color3::Location, "vertexColor"_s); /* Color4 is the same */ |
||||
if(configuration.flags() & Flag::ObjectId) { |
||||
out.bindFragmentDataLocation(ColorOutput, "color"_s); |
||||
out.bindFragmentDataLocation(ObjectIdOutput, "objectId"_s); |
||||
} |
||||
if(configuration.flags() >= Flag::InstancedObjectId) |
||||
out.bindAttributeLocation(ObjectId::Location, "instanceObjectId"_s); |
||||
if(configuration.flags() & Flag::InstancedTransformation) |
||||
out.bindAttributeLocation(TransformationMatrix::Location, "instancedTransformationMatrix"_s); |
||||
} |
||||
#endif |
||||
|
||||
out.submitLink(); |
||||
|
||||
return CompileState{std::move(out), std::move(vert), std::move(frag) |
||||
#ifndef MAGNUM_TARGET_GLES |
||||
, version |
||||
#endif |
||||
}; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>::LineGL(CompileState&& state): LineGL{static_cast<LineGL&&>(std::move(state))} { |
||||
#ifdef CORRADE_GRACEFUL_ASSERT |
||||
/* When graceful assertions fire from within compile(), we get a NoCreate'd
|
||||
CompileState. Exiting makes it possible to test the assert. */ |
||||
if(!id()) return; |
||||
#endif |
||||
|
||||
CORRADE_INTERNAL_ASSERT_OUTPUT(checkLink({GL::Shader(state._vert), GL::Shader(state._frag)})); |
||||
|
||||
const GL::Context& context = GL::Context::current(); |
||||
|
||||
#ifndef MAGNUM_TARGET_GLES |
||||
const GL::Version version = state._version; |
||||
if(!context.isExtensionSupported<GL::Extensions::ARB::explicit_uniform_location>(version)) |
||||
#endif |
||||
{ |
||||
_viewportSizeUniform = uniformLocation("viewportSize"_s); |
||||
if(_flags >= Flag::UniformBuffers) { |
||||
if(_drawCount > 1) |
||||
_drawOffsetUniform = uniformLocation("drawOffset"_s); |
||||
} else { |
||||
_transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"_s); |
||||
_widthUniform = uniformLocation("width"_s); |
||||
_smoothnessUniform = uniformLocation("smoothness"_s); |
||||
if(_joinStyle == LineJoinStyle::Miter) |
||||
_miterLimitUniform = uniformLocation("miterLimit"_s); |
||||
_backgroundColorUniform = uniformLocation("backgroundColor"_s); |
||||
_colorUniform = uniformLocation("color"_s); |
||||
if(_flags & Flag::ObjectId) |
||||
_objectIdUniform = uniformLocation("objectId"_s); |
||||
} |
||||
} |
||||
|
||||
#ifndef MAGNUM_TARGET_GLES |
||||
if(!context.isExtensionSupported<GL::Extensions::ARB::shading_language_420pack>(version)) |
||||
#endif |
||||
{ |
||||
if(_flags >= Flag::UniformBuffers) { |
||||
setUniformBlockBinding(uniformBlockIndex("TransformationProjection"_s), TransformationProjectionBufferBinding); |
||||
setUniformBlockBinding(uniformBlockIndex("Draw"_s), DrawBufferBinding); |
||||
setUniformBlockBinding(uniformBlockIndex("Material"_s), MaterialBufferBinding); |
||||
} |
||||
} |
||||
|
||||
/* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ |
||||
#ifdef MAGNUM_TARGET_GLES |
||||
if(_flags >= Flag::UniformBuffers) { |
||||
/* Draw offset is zero by default */ |
||||
} else { |
||||
setTransformationProjectionMatrix(MatrixTypeFor<dimensions, Float>{Math::IdentityInit}); |
||||
setWidth(1.0f); |
||||
/* Smoothness is zero by default */ |
||||
if(_joinStyle == LineJoinStyle::Miter) |
||||
setMiterLengthLimit(4.0f); |
||||
setColor(Magnum::Color4{1.0f}); |
||||
/* Object ID is zero by default */ |
||||
} |
||||
#endif |
||||
|
||||
static_cast<void>(context); |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>::LineGL(const Configuration& configuration): LineGL{compile(configuration)} {} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>::LineGL(NoInitT) {} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setViewportSize(const Vector2& size) { |
||||
setUniform(_viewportSizeUniform, size); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setTransformationProjectionMatrix(const MatrixTypeFor<dimensions, Float>& matrix) { |
||||
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), |
||||
"Shaders::LineGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled", *this); |
||||
setUniform(_transformationProjectionMatrixUniform, matrix); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setBackgroundColor(const Magnum::Color4& color) { |
||||
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), |
||||
"Shaders::LineGL::setBackgroundColor(): the shader was created with uniform buffers enabled", *this); |
||||
setUniform(_backgroundColorUniform, color); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setColor(const Magnum::Color4& color) { |
||||
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), |
||||
"Shaders::LineGL::setColor(): the shader was created with uniform buffers enabled", *this); |
||||
setUniform(_colorUniform, color); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setWidth(const Float width) { |
||||
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), |
||||
"Shaders::LineGL::setWidth(): the shader was created with uniform buffers enabled", *this); |
||||
setUniform(_widthUniform, width); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setSmoothness(const Float smoothness) { |
||||
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), |
||||
"Shaders::LineGL::setSmoothness(): the shader was created with uniform buffers enabled", *this); |
||||
setUniform(_smoothnessUniform, smoothness); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setMiterLengthLimit(const Float limit) { |
||||
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), |
||||
"Shaders::LineGL::setMiterLengthLimit(): the shader was created with uniform buffers enabled", *this); |
||||
CORRADE_ASSERT(_joinStyle == LineJoinStyle::Miter, |
||||
"Shaders::LineGL::setMiterLengthLimit(): the shader was created with" << _joinStyle, *this); |
||||
setUniform(_miterLimitUniform, Implementation::lineMiterLengthLimit("Shaders::LineGL::setMiterLengthLimit():", limit)); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setMiterAngleLimit(const Rad limit) { |
||||
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), |
||||
"Shaders::LineGL::setMiterAngleLimit(): the shader was created with uniform buffers enabled", *this); |
||||
CORRADE_ASSERT(_joinStyle == LineJoinStyle::Miter, |
||||
"Shaders::LineGL::setMiterAngleLimit(): the shader was created with" << _joinStyle, *this); |
||||
setUniform(_miterLimitUniform, Implementation::lineMiterAngleLimit("Shaders::LineGL::setMiterAngleLimit():", limit)); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setObjectId(UnsignedInt id) { |
||||
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers), |
||||
"Shaders::LineGL::setObjectId(): the shader was created with uniform buffers enabled", *this); |
||||
CORRADE_ASSERT(_flags & Flag::ObjectId, |
||||
"Shaders::LineGL::setObjectId(): the shader was not created with object ID enabled", *this); |
||||
setUniform(_objectIdUniform, id); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::setDrawOffset(const UnsignedInt offset) { |
||||
CORRADE_ASSERT(_flags >= Flag::UniformBuffers, |
||||
"Shaders::LineGL::setDrawOffset(): the shader was not created with uniform buffers enabled", *this); |
||||
CORRADE_ASSERT(offset < _drawCount, |
||||
"Shaders::LineGL::setDrawOffset(): draw offset" << offset << "is out of bounds for" << _drawCount << "draws", *this); |
||||
if(_drawCount > 1) setUniform(_drawOffsetUniform, offset); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::bindTransformationProjectionBuffer(GL::Buffer& buffer) { |
||||
CORRADE_ASSERT(_flags >= Flag::UniformBuffers, |
||||
"Shaders::LineGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); |
||||
buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::bindTransformationProjectionBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { |
||||
CORRADE_ASSERT(_flags >= Flag::UniformBuffers, |
||||
"Shaders::LineGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled", *this); |
||||
buffer.bind(GL::Buffer::Target::Uniform, TransformationProjectionBufferBinding, offset, size); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::bindDrawBuffer(GL::Buffer& buffer) { |
||||
CORRADE_ASSERT(_flags >= Flag::UniformBuffers, |
||||
"Shaders::LineGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); |
||||
buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::bindDrawBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { |
||||
CORRADE_ASSERT(_flags >= Flag::UniformBuffers, |
||||
"Shaders::LineGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled", *this); |
||||
buffer.bind(GL::Buffer::Target::Uniform, DrawBufferBinding, offset, size); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::bindMaterialBuffer(GL::Buffer& buffer) { |
||||
CORRADE_ASSERT(_flags >= Flag::UniformBuffers, |
||||
"Shaders::LineGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); |
||||
buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>& LineGL<dimensions>::bindMaterialBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) { |
||||
CORRADE_ASSERT(_flags >= Flag::UniformBuffers, |
||||
"Shaders::LineGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled", *this); |
||||
buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size); |
||||
return *this; |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> LineGL<dimensions>::Configuration::Configuration(): _capStyle{LineCapStyle::Square}, _joinStyle{LineJoinStyle::Miter} {} |
||||
|
||||
template class MAGNUM_SHADERS_EXPORT LineGL<2>; |
||||
template class MAGNUM_SHADERS_EXPORT LineGL<3>; |
||||
|
||||
namespace Implementation { |
||||
|
||||
Debug& operator<<(Debug& debug, const LineGLFlag value) { |
||||
debug << "Shaders::LineGL::Flag" << Debug::nospace; |
||||
|
||||
switch(value) { |
||||
/* LCOV_EXCL_START */ |
||||
#define _c(v) case LineGLFlag::v: return debug << "::" #v; |
||||
_c(VertexColor) |
||||
_c(ObjectId) |
||||
_c(InstancedObjectId) |
||||
_c(InstancedTransformation) |
||||
_c(UniformBuffers) |
||||
_c(MultiDraw) |
||||
#undef _c |
||||
/* LCOV_EXCL_STOP */ |
||||
} |
||||
|
||||
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedShort(value)) << Debug::nospace << ")"; |
||||
} |
||||
|
||||
Debug& operator<<(Debug& debug, const LineGLFlags value) { |
||||
return Containers::enumSetDebugOutput(debug, value, "Shaders::LineGL::Flags{}", { |
||||
LineGLFlag::VertexColor, |
||||
LineGLFlag::InstancedObjectId, /* Superset of ObjectId */ |
||||
LineGLFlag::ObjectId, |
||||
LineGLFlag::InstancedTransformation, |
||||
LineGLFlag::MultiDraw, /* Superset of UniformBuffers */ |
||||
LineGLFlag::UniformBuffers |
||||
}); |
||||
} |
||||
|
||||
} |
||||
|
||||
}} |
||||
@ -0,0 +1,157 @@
|
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
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. |
||||
*/ |
||||
|
||||
#include <sstream> |
||||
#include <Corrade/Containers/String.h> |
||||
#include <Corrade/TestSuite/Tester.h> |
||||
#include <Corrade/Utility/DebugStl.h> |
||||
#include <Corrade/Utility/Format.h> |
||||
|
||||
#include "Magnum/Shaders/LineGL.h" |
||||
/* Yes, really */ |
||||
#include "Magnum/Shaders/generic.glsl" |
||||
|
||||
namespace Magnum { namespace Shaders { namespace Test { namespace { |
||||
|
||||
/* There's an underscore between GL and Test to disambiguate from GLTest, which
|
||||
is a common suffix used to mark tests that need a GL context. Ugly, I know. */ |
||||
struct LineGL_Test: TestSuite::Tester { |
||||
explicit LineGL_Test(); |
||||
|
||||
void glslAttributeMatch(); |
||||
|
||||
template<UnsignedInt dimensions> void constructConfigurationDefault(); |
||||
template<UnsignedInt dimensions> void constructConfigurationSetters(); |
||||
|
||||
template<UnsignedInt dimensions> void constructNoCreate(); |
||||
template<UnsignedInt dimensions> void constructCopy(); |
||||
|
||||
void debugFlag(); |
||||
void debugFlags(); |
||||
void debugFlagsSupersets(); |
||||
}; |
||||
|
||||
LineGL_Test::LineGL_Test() { |
||||
addTests({&LineGL_Test::glslAttributeMatch, |
||||
|
||||
&LineGL_Test::constructConfigurationDefault<2>, |
||||
&LineGL_Test::constructConfigurationDefault<3>, |
||||
&LineGL_Test::constructConfigurationSetters<2>, |
||||
&LineGL_Test::constructConfigurationSetters<3>, |
||||
|
||||
&LineGL_Test::constructNoCreate<2>, |
||||
&LineGL_Test::constructNoCreate<3>, |
||||
&LineGL_Test::constructCopy<2>, |
||||
&LineGL_Test::constructCopy<3>, |
||||
|
||||
&LineGL_Test::debugFlag, |
||||
&LineGL_Test::debugFlags, |
||||
&LineGL_Test::debugFlagsSupersets}); |
||||
} |
||||
|
||||
void LineGL_Test::glslAttributeMatch() { |
||||
/* Position, Color and ObjectId tested in GenericGL_Test */ |
||||
|
||||
CORRADE_COMPARE(LINE_PREVIOUS_POSITION_ATTRIBUTE_LOCATION, LineGL2D::PreviousPosition::Location); |
||||
CORRADE_COMPARE(LINE_PREVIOUS_POSITION_ATTRIBUTE_LOCATION, LineGL3D::PreviousPosition::Location); |
||||
|
||||
CORRADE_COMPARE(LINE_NEXT_POSITION_ATTRIBUTE_LOCATION, LineGL2D::NextPosition::Location); |
||||
CORRADE_COMPARE(LINE_NEXT_POSITION_ATTRIBUTE_LOCATION, LineGL3D::NextPosition::Location); |
||||
|
||||
CORRADE_COMPARE(LINE_ANNOTATION_ATTRIBUTE_LOCATION, LineGL2D::Annotation::Location); |
||||
CORRADE_COMPARE(LINE_ANNOTATION_ATTRIBUTE_LOCATION, LineGL3D::Annotation::Location); |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> void LineGL_Test::constructConfigurationDefault() { |
||||
setTestCaseTemplateName(Utility::format("{}", dimensions)); |
||||
|
||||
typename LineGL<dimensions>::Configuration configuration; |
||||
CORRADE_COMPARE(configuration.flags(), typename LineGL<dimensions>::Flags{}); |
||||
CORRADE_COMPARE(configuration.materialCount(), 1); |
||||
CORRADE_COMPARE(configuration.drawCount(), 1); |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> void LineGL_Test::constructConfigurationSetters() { |
||||
setTestCaseTemplateName(Utility::format("{}", dimensions)); |
||||
|
||||
typename LineGL<dimensions>::Configuration configuration = typename LineGL<dimensions>::Configuration{} |
||||
.setFlags(LineGL<dimensions>::Flag::VertexColor) |
||||
.setMaterialCount(17) |
||||
.setDrawCount(266); |
||||
CORRADE_COMPARE(configuration.flags(), LineGL<dimensions>::Flag::VertexColor); |
||||
CORRADE_COMPARE(configuration.materialCount(), 17); |
||||
CORRADE_COMPARE(configuration.drawCount(), 266); |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> void LineGL_Test::constructNoCreate() { |
||||
setTestCaseTemplateName(Utility::format("{}", dimensions)); |
||||
|
||||
{ |
||||
LineGL<dimensions> shader{NoCreate}; |
||||
CORRADE_COMPARE(shader.id(), 0); |
||||
CORRADE_COMPARE(shader.flags(), typename LineGL<dimensions>::Flags{}); |
||||
} |
||||
|
||||
CORRADE_VERIFY(true); |
||||
} |
||||
|
||||
template<UnsignedInt dimensions> void LineGL_Test::constructCopy() { |
||||
setTestCaseTemplateName(Utility::format("{}", dimensions)); |
||||
|
||||
CORRADE_VERIFY(!std::is_copy_constructible<LineGL<dimensions>>{}); |
||||
CORRADE_VERIFY(!std::is_copy_assignable<LineGL<dimensions>>{}); |
||||
} |
||||
|
||||
void LineGL_Test::debugFlag() { |
||||
std::ostringstream out; |
||||
Debug{&out} << LineGL3D::Flag::VertexColor << LineGL3D::Flag(0xf00d); |
||||
CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::VertexColor Shaders::LineGL::Flag(0xf00d)\n"); |
||||
} |
||||
|
||||
void LineGL_Test::debugFlags() { |
||||
std::ostringstream out; |
||||
Debug{&out} << (LineGL3D::Flag::VertexColor|LineGL3D::Flag::InstancedTransformation) << LineGL3D::Flags{}; |
||||
CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::VertexColor|Shaders::LineGL::Flag::InstancedTransformation Shaders::LineGL::Flags{}\n"); |
||||
} |
||||
|
||||
void LineGL_Test::debugFlagsSupersets() { |
||||
/* InstancedObjectId is a superset of ObjectId so only one should be
|
||||
printed */ |
||||
{ |
||||
std::ostringstream out; |
||||
Debug{&out} << (LineGL3D::Flag::ObjectId|LineGL3D::Flag::InstancedObjectId); |
||||
CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::InstancedObjectId\n"); |
||||
|
||||
/* MultiDraw is a superset of UniformBuffers so only one should be printed */ |
||||
} { |
||||
std::ostringstream out; |
||||
Debug{&out} << (LineGL3D::Flag::MultiDraw|LineGL3D::Flag::UniformBuffers); |
||||
CORRADE_COMPARE(out.str(), "Shaders::LineGL::Flag::MultiDraw\n"); |
||||
} |
||||
} |
||||
|
||||
}}}} |
||||
|
||||
CORRADE_TEST_MAIN(Magnum::Shaders::Test::LineGL_Test) |
||||
@ -0,0 +1,306 @@
|
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
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. |
||||
*/ |
||||
|
||||
#include <new> |
||||
#include <sstream> |
||||
#include <Corrade/TestSuite/Tester.h> |
||||
#include <Corrade/Utility/DebugStl.h> |
||||
#include <Corrade/Utility/FormatStl.h> |
||||
|
||||
#include "Magnum/Shaders/Line.h" |
||||
#include "Magnum/Shaders/Implementation/lineMiterLimit.h" |
||||
|
||||
namespace Magnum { namespace Shaders { namespace Test { namespace { |
||||
|
||||
struct LineTest: TestSuite::Tester { |
||||
explicit LineTest(); |
||||
|
||||
template<class T> void uniformSizeAlignment(); |
||||
|
||||
void drawUniformConstructDefault(); |
||||
void drawUniformConstructNoInit(); |
||||
void drawUniformSetters(); |
||||
void drawUniformMaterialIdPacking(); |
||||
|
||||
void materialUniformConstructDefault(); |
||||
void materialUniformConstructNoInit(); |
||||
void materialUniformSetters(); |
||||
|
||||
void materialUniformSetMiterLengthLimitInvalid(); |
||||
void materialUniformSetMiterAngleLimitInvalid(); |
||||
|
||||
void debugCapStyle(); |
||||
void debugJoinStyle(); |
||||
}; |
||||
|
||||
using namespace Math::Literals; |
||||
|
||||
const struct { |
||||
const char* name; |
||||
Float limit; |
||||
const char* message; |
||||
} MaterialUniformSetMiterLengthLimitInvalidData[]{ |
||||
{"too short", 0.9997f, |
||||
"expected a finite value greater than or equal to 1, got 0.9997"}, |
||||
{"too long", Constants::inf(), |
||||
"expected a finite value greater than or equal to 1, got inf"}, |
||||
}; |
||||
|
||||
const struct { |
||||
const char* name; |
||||
Rad limit; |
||||
const char* message; |
||||
} MaterialUniformSetMiterAngleLimitInvalidData[]{ |
||||
{"too small", 0.0_degf, |
||||
"expected a value greater than 0° and less than or equal to 180°, got 0°"}, |
||||
{"too large", 180.1_degf, |
||||
"expected a value greater than 0° and less than or equal to 180°, got 180.1°"} |
||||
}; |
||||
|
||||
LineTest::LineTest() { |
||||
addTests({&LineTest::uniformSizeAlignment<LineDrawUniform>, |
||||
&LineTest::uniformSizeAlignment<LineMaterialUniform>, |
||||
|
||||
&LineTest::drawUniformConstructDefault, |
||||
&LineTest::drawUniformConstructNoInit, |
||||
&LineTest::drawUniformSetters, |
||||
&LineTest::drawUniformMaterialIdPacking, |
||||
|
||||
&LineTest::materialUniformConstructDefault, |
||||
&LineTest::materialUniformConstructNoInit, |
||||
&LineTest::materialUniformSetters}); |
||||
|
||||
addInstancedTests({&LineTest::materialUniformSetMiterLengthLimitInvalid}, |
||||
Containers::arraySize(MaterialUniformSetMiterLengthLimitInvalidData)); |
||||
|
||||
addInstancedTests({&LineTest::materialUniformSetMiterAngleLimitInvalid}, |
||||
Containers::arraySize(MaterialUniformSetMiterAngleLimitInvalidData)); |
||||
|
||||
addTests({&LineTest::debugCapStyle, |
||||
&LineTest::debugJoinStyle}); |
||||
} |
||||
|
||||
template<class> struct UniformTraits; |
||||
template<> struct UniformTraits<LineDrawUniform> { |
||||
static const char* name() { return "LineDrawUniform"; } |
||||
}; |
||||
template<> struct UniformTraits<LineMaterialUniform> { |
||||
static const char* name() { return "LineMaterialUniform"; } |
||||
}; |
||||
|
||||
template<class T> void LineTest::uniformSizeAlignment() { |
||||
setTestCaseTemplateName(UniformTraits<T>::name()); |
||||
|
||||
CORRADE_FAIL_IF(sizeof(T) % sizeof(Vector4) != 0, sizeof(T) << "is not a multiple of vec4 for UBO alignment."); |
||||
|
||||
/* 48-byte structures are fine, we'll align them to 768 bytes and not
|
||||
256, but warn about that */ |
||||
CORRADE_FAIL_IF(768 % sizeof(T) != 0, sizeof(T) << "can't fit exactly into 768-byte UBO alignment."); |
||||
if(256 % sizeof(T) != 0) |
||||
CORRADE_WARN(sizeof(T) << "can't fit exactly into 256-byte UBO alignment, only 768."); |
||||
|
||||
CORRADE_COMPARE(alignof(T), 4); |
||||
} |
||||
|
||||
void LineTest::drawUniformConstructDefault() { |
||||
LineDrawUniform a; |
||||
LineDrawUniform b{DefaultInit}; |
||||
CORRADE_COMPARE(a.materialId, 0); |
||||
CORRADE_COMPARE(b.materialId, 0); |
||||
CORRADE_COMPARE(a.objectId, 0); |
||||
CORRADE_COMPARE(b.objectId, 0); |
||||
|
||||
constexpr LineDrawUniform ca; |
||||
constexpr LineDrawUniform cb{DefaultInit}; |
||||
CORRADE_COMPARE(ca.materialId, 0); |
||||
CORRADE_COMPARE(cb.materialId, 0); |
||||
CORRADE_COMPARE(ca.objectId, 0); |
||||
CORRADE_COMPARE(cb.objectId, 0); |
||||
|
||||
CORRADE_VERIFY(std::is_nothrow_default_constructible<LineDrawUniform>::value); |
||||
CORRADE_VERIFY(std::is_nothrow_constructible<LineDrawUniform, DefaultInitT>::value); |
||||
|
||||
/* Implicit construction is not allowed */ |
||||
CORRADE_VERIFY(!std::is_convertible<DefaultInitT, LineDrawUniform>::value); |
||||
} |
||||
|
||||
void LineTest::drawUniformConstructNoInit() { |
||||
/* Testing only some fields, should be enough */ |
||||
LineDrawUniform a; |
||||
a.materialId = 5; |
||||
a.objectId = 7; |
||||
|
||||
new(&a) LineDrawUniform{NoInit}; |
||||
{ |
||||
/* Explicitly check we're not on Clang because certain Clang-based IDEs
|
||||
inherit __GNUC__ if GCC is used instead of leaving it at 4 like |
||||
Clang itself does */ |
||||
#if defined(CORRADE_TARGET_GCC) && !defined(CORRADE_TARGET_CLANG) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ |
||||
CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); |
||||
#endif |
||||
CORRADE_COMPARE(a.materialId, 5); |
||||
CORRADE_COMPARE(a.objectId, 7); |
||||
} |
||||
|
||||
CORRADE_VERIFY(std::is_nothrow_constructible<LineDrawUniform, NoInitT>::value); |
||||
|
||||
/* Implicit construction is not allowed */ |
||||
CORRADE_VERIFY(!std::is_convertible<NoInitT, LineDrawUniform>::value); |
||||
} |
||||
|
||||
void LineTest::drawUniformSetters() { |
||||
LineDrawUniform a; |
||||
a.setMaterialId(5) |
||||
.setObjectId(7); |
||||
CORRADE_COMPARE(a.materialId, 5); |
||||
CORRADE_COMPARE(a.objectId, 7); |
||||
} |
||||
|
||||
void LineTest::drawUniformMaterialIdPacking() { |
||||
LineDrawUniform a; |
||||
a.setMaterialId(13765); |
||||
/* materialId should be right at the beginning, in the low 16 bits on both
|
||||
LE and BE */ |
||||
CORRADE_COMPARE(reinterpret_cast<UnsignedInt*>(&a)[0] & 0xffff, 13765); |
||||
} |
||||
|
||||
void LineTest::materialUniformConstructDefault() { |
||||
LineMaterialUniform a; |
||||
LineMaterialUniform b{DefaultInit}; |
||||
CORRADE_COMPARE(a.backgroundColor, 0x00000000_rgbaf); |
||||
CORRADE_COMPARE(b.backgroundColor, 0x00000000_rgbaf); |
||||
CORRADE_COMPARE(a.color, 0xffffffff_rgbaf); |
||||
CORRADE_COMPARE(b.color, 0xffffffff_rgbaf); |
||||
CORRADE_COMPARE(a.width, 1.0f); |
||||
CORRADE_COMPARE(b.width, 1.0f); |
||||
CORRADE_COMPARE(a.smoothness, 0.0f); |
||||
CORRADE_COMPARE(b.smoothness, 0.0f); |
||||
CORRADE_COMPARE(a.miterLimit, Implementation::lineMiterLengthLimit("", 4.0f)); |
||||
CORRADE_COMPARE(b.miterLimit, Implementation::lineMiterLengthLimit("", 4.0f)); |
||||
|
||||
constexpr LineMaterialUniform ca; |
||||
constexpr LineMaterialUniform cb{DefaultInit}; |
||||
CORRADE_COMPARE(ca.backgroundColor, 0x00000000_rgbaf); |
||||
CORRADE_COMPARE(cb.backgroundColor, 0x00000000_rgbaf); |
||||
CORRADE_COMPARE(ca.color, 0xffffffff_rgbaf); |
||||
CORRADE_COMPARE(cb.color, 0xffffffff_rgbaf); |
||||
CORRADE_COMPARE(ca.width, 1.0f); |
||||
CORRADE_COMPARE(cb.width, 1.0f); |
||||
CORRADE_COMPARE(ca.smoothness, 0.0f); |
||||
CORRADE_COMPARE(cb.smoothness, 0.0f); |
||||
CORRADE_COMPARE(ca.miterLimit, Implementation::lineMiterLengthLimit("", 4.0f)); |
||||
CORRADE_COMPARE(cb.miterLimit, Implementation::lineMiterLengthLimit("", 4.0f)); |
||||
|
||||
CORRADE_VERIFY(std::is_nothrow_default_constructible<LineDrawUniform>::value); |
||||
CORRADE_VERIFY(std::is_nothrow_constructible<LineDrawUniform, DefaultInitT>::value); |
||||
|
||||
/* Implicit construction is not allowed */ |
||||
CORRADE_VERIFY(!std::is_convertible<DefaultInitT, LineDrawUniform>::value); |
||||
} |
||||
|
||||
void LineTest::materialUniformConstructNoInit() { |
||||
/* Testing only some fields, should be enough */ |
||||
LineMaterialUniform a; |
||||
a.color = 0x354565fc_rgbaf; |
||||
a.smoothness = 7.0f; |
||||
|
||||
new(&a) LineMaterialUniform{NoInit}; |
||||
{ |
||||
/* Explicitly check we're not on Clang because certain Clang-based IDEs
|
||||
inherit __GNUC__ if GCC is used instead of leaving it at 4 like |
||||
Clang itself does */ |
||||
#if defined(CORRADE_TARGET_GCC) && !defined(CORRADE_TARGET_CLANG) && __GNUC__*100 + __GNUC_MINOR__ >= 601 && __OPTIMIZE__ |
||||
CORRADE_EXPECT_FAIL("GCC 6.1+ misoptimizes and overwrites the value."); |
||||
#endif |
||||
CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); |
||||
CORRADE_COMPARE(a.smoothness, 7.0f); |
||||
} |
||||
|
||||
CORRADE_VERIFY(std::is_nothrow_constructible<LineMaterialUniform, NoInitT>::value); |
||||
|
||||
/* Implicit construction is not allowed */ |
||||
CORRADE_VERIFY(!std::is_convertible<NoInitT, LineMaterialUniform>::value); |
||||
} |
||||
|
||||
void LineTest::materialUniformSetters() { |
||||
LineMaterialUniform a; |
||||
a.setBackgroundColor(0x01020304_rgbaf) |
||||
.setColor(0x354565fc_rgbaf) |
||||
.setWidth(2.5f) |
||||
.setSmoothness(7.0f) |
||||
.setMiterLengthLimit(25.0f); |
||||
CORRADE_COMPARE(a.backgroundColor, 0x01020304_rgbaf); |
||||
CORRADE_COMPARE(a.color, 0x354565fc_rgbaf); |
||||
CORRADE_COMPARE(a.width, 2.5f); |
||||
CORRADE_COMPARE(a.smoothness, 7.0f); |
||||
CORRADE_COMPARE(a.miterLimit, 0.9968f); |
||||
|
||||
a.setMiterAngleLimit(35.0_degf); |
||||
CORRADE_COMPARE(a.miterLimit, 0.819152f); |
||||
} |
||||
|
||||
void LineTest::materialUniformSetMiterLengthLimitInvalid() { |
||||
auto&& data = MaterialUniformSetMiterLengthLimitInvalidData[testCaseInstanceId()]; |
||||
setTestCaseDescription(data.name); |
||||
|
||||
CORRADE_SKIP_IF_NO_ASSERT(); |
||||
|
||||
LineMaterialUniform a; |
||||
|
||||
std::ostringstream out; |
||||
Error redirectError{&out}; |
||||
a.setMiterLengthLimit(data.limit); |
||||
CORRADE_COMPARE(out.str(), Utility::formatString("Shaders::LineMaterialUniform::setMiterLengthLimit(): {}\n", data.message)); |
||||
} |
||||
|
||||
void LineTest::materialUniformSetMiterAngleLimitInvalid() { |
||||
auto&& data = MaterialUniformSetMiterAngleLimitInvalidData[testCaseInstanceId()]; |
||||
setTestCaseDescription(data.name); |
||||
|
||||
CORRADE_SKIP_IF_NO_ASSERT(); |
||||
|
||||
LineMaterialUniform a; |
||||
|
||||
std::ostringstream out; |
||||
Error redirectError{&out}; |
||||
a.setMiterAngleLimit(data.limit); |
||||
CORRADE_COMPARE(out.str(), Utility::formatString("Shaders::LineMaterialUniform::setMiterAngleLimit(): {}\n", data.message)); |
||||
} |
||||
|
||||
void LineTest::debugCapStyle() { |
||||
std::ostringstream out; |
||||
Debug{&out} << LineCapStyle::Square << LineCapStyle(0xb0); |
||||
CORRADE_COMPARE(out.str(), "Shaders::LineCapStyle::Square Shaders::LineCapStyle(0xb0)\n"); |
||||
} |
||||
|
||||
void LineTest::debugJoinStyle() { |
||||
std::ostringstream out; |
||||
Debug{&out} << LineJoinStyle::Bevel << LineJoinStyle(0xb0); |
||||
CORRADE_COMPARE(out.str(), "Shaders::LineJoinStyle::Bevel Shaders::LineJoinStyle(0xb0)\n"); |
||||
} |
||||
|
||||
}}}} |
||||
|
||||
CORRADE_TEST_MAIN(Magnum::Shaders::Test::LineTest) |
||||