mirror of https://github.com/mosra/magnum.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2761 lines
117 KiB
2761 lines
117 KiB
|
4 years ago
|
/*
|
||
|
|
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/GrowableArray.h>
|
||
|
|
#include <Corrade/Containers/Iterable.h>
|
||
|
|
#include <Corrade/Containers/StridedArrayView.h>
|
||
|
|
#include <Corrade/Containers/String.h>
|
||
|
|
#include <Corrade/Containers/StringIterable.h>
|
||
|
|
#include <Corrade/PluginManager/Manager.h>
|
||
|
|
#include <Corrade/Utility/DebugStl.h>
|
||
|
|
#include <Corrade/Utility/FormatStl.h>
|
||
|
|
#include <Corrade/Utility/Path.h>
|
||
|
|
#include <Corrade/Utility/System.h>
|
||
|
|
|
||
|
|
#include "Magnum/Image.h"
|
||
|
|
#include "Magnum/PixelFormat.h"
|
||
|
|
#include "Magnum/DebugTools/CompareImage.h"
|
||
|
|
#include "Magnum/GL/Buffer.h"
|
||
|
|
#include "Magnum/GL/Extensions.h"
|
||
|
|
#include "Magnum/GL/Framebuffer.h"
|
||
|
|
#include "Magnum/GL/Mesh.h"
|
||
|
|
#include "Magnum/GL/MeshView.h"
|
||
|
|
#include "Magnum/GL/OpenGLTester.h"
|
||
|
|
#include "Magnum/GL/Renderbuffer.h"
|
||
|
|
#include "Magnum/GL/RenderbufferFormat.h"
|
||
|
|
#include "Magnum/Math/Color.h"
|
||
|
|
#include "Magnum/Math/Complex.h"
|
||
|
|
#include "Magnum/Math/FunctionsBatch.h"
|
||
|
|
#include "Magnum/Math/Matrix3.h"
|
||
|
|
#include "Magnum/Math/Matrix4.h"
|
||
|
|
#include "Magnum/MeshTools/Compile.h"
|
||
|
|
#include "Magnum/Primitives/Cube.h"
|
||
|
|
#include "Magnum/Shaders/PhongGL.h" /* for testing correct depth in 3D */
|
||
|
|
#include "Magnum/Shaders/Generic.h"
|
||
|
|
#include "Magnum/Shaders/Line.h"
|
||
|
|
#include "Magnum/Shaders/LineGL.h"
|
||
|
|
#include "Magnum/Trade/AbstractImporter.h"
|
||
|
|
#include "Magnum/Trade/MeshData.h"
|
||
|
|
|
||
|
|
#include "configure.h"
|
||
|
|
|
||
|
|
namespace Magnum { namespace Shaders { namespace Test { namespace {
|
||
|
|
|
||
|
|
struct LineGLTest: GL::OpenGLTester {
|
||
|
|
explicit LineGLTest();
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void construct();
|
||
|
|
template<UnsignedInt dimensions> void constructAsync();
|
||
|
|
template<UnsignedInt dimensions> void constructUniformBuffers();
|
||
|
|
template<UnsignedInt dimensions> void constructUniformBuffersAsync();
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void constructMove();
|
||
|
|
template<UnsignedInt dimensions> void constructMoveUniformBuffers();
|
||
|
|
|
||
|
|
/* No constructInvalid() as there isn't any assertion in the constructor
|
||
|
|
that wouldn't be related to UBOs */
|
||
|
|
template<UnsignedInt dimensions> void constructUniformBuffersInvalid();
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void setUniformUniformBuffersEnabled();
|
||
|
|
template<UnsignedInt dimensions> void bindBufferUniformBuffersNotEnabled();
|
||
|
|
template<UnsignedInt dimensions> void setMiterLengthLimitInvalid();
|
||
|
|
template<UnsignedInt dimensions> void setMiterAngleLimitInvalid();
|
||
|
|
template<UnsignedInt dimensions> void setObjectIdNotEnabled();
|
||
|
|
template<UnsignedInt dimensions> void setWrongDrawOffset();
|
||
|
|
|
||
|
|
void renderSetupLarge();
|
||
|
|
void renderSetupSmall();
|
||
|
|
void renderTeardown();
|
||
|
|
|
||
|
|
template<LineGL2D::Flag flag = LineGL2D::Flag{}> void renderDefaults2D();
|
||
|
|
template<LineGL3D::Flag flag = LineGL3D::Flag{}> void renderDefaults3D();
|
||
|
|
|
||
|
|
template<LineGL2D::Flag flag = LineGL2D::Flag{}> void renderLineCapsJoins2D();
|
||
|
|
/* These test the shader algorithms, which are independent of UBOs */
|
||
|
|
void renderLineCapsJoins2DReversed();
|
||
|
|
void renderLineCapsJoins2DTransformed();
|
||
|
|
|
||
|
|
template<LineGL3D::Flag flag = LineGL3D::Flag{}> void renderCube3D();
|
||
|
|
/* This test the shader algorithms, which are independent of UBOs */
|
||
|
|
void renderPerspective3D();
|
||
|
|
|
||
|
|
template<class T, LineGL2D::Flag flag = LineGL2D::Flag{}> void renderVertexColor2D();
|
||
|
|
template<class T, LineGL3D::Flag flag = LineGL3D::Flag{}> void renderVertexColor3D();
|
||
|
|
|
||
|
|
template<LineGL2D::Flag flag = LineGL2D::Flag{}> void renderObjectId2D();
|
||
|
|
template<LineGL3D::Flag flag = LineGL3D::Flag{}> void renderObjectId3D();
|
||
|
|
|
||
|
|
template<LineGL2D::Flag flag = LineGL2D::Flag{}> void renderInstanced2D();
|
||
|
|
template<LineGL3D::Flag flag = LineGL3D::Flag{}> void renderInstanced3D();
|
||
|
|
|
||
|
|
void renderMulti2D();
|
||
|
|
void renderMulti3D();
|
||
|
|
|
||
|
|
private:
|
||
|
|
PluginManager::Manager<Trade::AbstractImporter> _manager{"nonexistent"};
|
||
|
|
|
||
|
|
GL::Renderbuffer _color{NoCreate};
|
||
|
|
GL::Renderbuffer _depth{NoCreate};
|
||
|
|
GL::Renderbuffer _objectId{NoCreate};
|
||
|
|
GL::Framebuffer _framebuffer{NoCreate};
|
||
|
|
};
|
||
|
|
|
||
|
|
using namespace Containers::Literals;
|
||
|
|
using namespace Math::Literals;
|
||
|
|
|
||
|
|
const struct {
|
||
|
|
const char* name;
|
||
|
|
LineGL2D::Flags flags;
|
||
|
|
Containers::Optional<LineCapStyle> capStyle;
|
||
|
|
Containers::Optional<LineJoinStyle> joinStyle;
|
||
|
|
} ConstructData[]{
|
||
|
|
{"", {}, {}, {}},
|
||
|
|
{"square caps, bevel joins", {}, LineCapStyle::Square, LineJoinStyle::Bevel},
|
||
|
|
{"round caps, miter joins", {}, LineCapStyle::Round, LineJoinStyle::Miter},
|
||
|
|
/** @todo use JoinStyle::MiterClip once it exists */
|
||
|
|
{"butt caps, miter joins", {}, LineCapStyle::Butt, LineJoinStyle::Miter},
|
||
|
|
/** @todo use JoinStyle::Round once it exists */
|
||
|
|
{"triangle caps, miter joins", {}, LineCapStyle::Triangle, LineJoinStyle::Miter},
|
||
|
|
{"vertex colors", LineGL2D::Flag::VertexColor, {}, {}},
|
||
|
|
{"object ID", LineGL2D::Flag::ObjectId, {}, {}},
|
||
|
|
{"instanced object ID", LineGL2D::Flag::InstancedObjectId, {}, {}},
|
||
|
|
{"instanced transformation", LineGL2D::Flag::InstancedTransformation, {}, {}},
|
||
|
|
};
|
||
|
|
|
||
|
|
const struct {
|
||
|
|
const char* name;
|
||
|
|
LineGL2D::Flags flags;
|
||
|
|
Containers::Optional<LineCapStyle> capStyle;
|
||
|
|
Containers::Optional<LineJoinStyle> joinStyle;
|
||
|
|
UnsignedInt materialCount, drawCount;
|
||
|
|
} ConstructUniformBuffersData[]{
|
||
|
|
{"classic fallback", {}, {}, {}, 1, 1},
|
||
|
|
{"", LineGL2D::Flag::UniformBuffers, {}, {}, 1, 1},
|
||
|
|
/* Just to verify that access to the miter limits is properly guarded,
|
||
|
|
no need to check all variants */
|
||
|
|
{"round caps, miter joins", LineGL2D::Flag::UniformBuffers, LineCapStyle::Round, LineJoinStyle::Miter, 1, 1},
|
||
|
|
{"butt caps, bevel joins", LineGL2D::Flag::UniformBuffers, LineCapStyle::Butt, LineJoinStyle::Bevel, 1, 1},
|
||
|
|
/* SwiftShader has 256 uniform vectors at most, per-draw is 4+1 in 3D case
|
||
|
|
and 3+1 in 2D, per-material 3 */
|
||
|
|
{"multiple materials, draws", LineGL2D::Flag::UniformBuffers, {}, {}, 16, 41},
|
||
|
|
{"object ID", LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::ObjectId, {}, {}, 1, 1},
|
||
|
|
{"instanced object ID", LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::InstancedObjectId, {}, {}, 1, 1},
|
||
|
|
{"multidraw with all the things", LineGL2D::Flag::MultiDraw|LineGL2D::Flag::ObjectId|LineGL2D::Flag::InstancedTransformation|LineGL2D::Flag::InstancedObjectId, {}, {}, 16, 41}
|
||
|
|
};
|
||
|
|
|
||
|
|
const struct {
|
||
|
|
const char* name;
|
||
|
|
LineGL2D::Flags flags;
|
||
|
|
UnsignedInt materialCount, drawCount;
|
||
|
|
const char* message;
|
||
|
|
} ConstructUniformBuffersInvalidData[]{
|
||
|
|
{"zero draws", LineGL2D::Flag::UniformBuffers, 1, 0,
|
||
|
|
"draw count can't be zero"},
|
||
|
|
{"zero materials", LineGL2D::Flag::UniformBuffers, 0, 1,
|
||
|
|
"material count can't be zero"}
|
||
|
|
};
|
||
|
|
|
||
|
|
const struct {
|
||
|
|
const char* name;
|
||
|
|
LineJoinStyle joinStyle;
|
||
|
|
Float limit;
|
||
|
|
const char* message;
|
||
|
|
} SetMiterLengthLimitInvalidData[]{
|
||
|
|
{"wrong join style", LineJoinStyle::Bevel, 1.0f,
|
||
|
|
"the shader was created with Shaders::LineJoinStyle::Bevel"},
|
||
|
|
{"too short", LineJoinStyle::Miter, 0.9997f,
|
||
|
|
"expected a finite value greater than or equal to 1, got 0.9997"},
|
||
|
|
{"too long", LineJoinStyle::Miter, Constants::inf(),
|
||
|
|
"expected a finite value greater than or equal to 1, got inf"},
|
||
|
|
};
|
||
|
|
|
||
|
|
const struct {
|
||
|
|
const char* name;
|
||
|
|
LineJoinStyle joinStyle;
|
||
|
|
Rad limit;
|
||
|
|
const char* message;
|
||
|
|
} SetMiterAngleLimitInvalidData[]{
|
||
|
|
{"wrong join style", LineJoinStyle::Bevel, 90.0_degf,
|
||
|
|
"the shader was created with Shaders::LineJoinStyle::Bevel"},
|
||
|
|
{"too small", LineJoinStyle::Miter, 0.0_degf,
|
||
|
|
"expected a value greater than 0° and less than or equal to 180°, got 0°"},
|
||
|
|
{"too large", LineJoinStyle::Miter, 180.1_degf,
|
||
|
|
"expected a value greater than 0° and less than or equal to 180°, got 180.1°"}
|
||
|
|
};
|
||
|
|
|
||
|
|
const struct {
|
||
|
|
const char* name;
|
||
|
|
Float width;
|
||
|
|
Float smoothness;
|
||
|
|
Containers::Optional<Float> miterLengthLimit;
|
||
|
|
Containers::Optional<Deg> miterAngleLimit;
|
||
|
|
Containers::Optional<LineCapStyle> capStyle;
|
||
|
|
Containers::Optional<LineJoinStyle> joinStyle;
|
||
|
|
const char* expected;
|
||
|
|
} RenderLineCapsJoins2DData[]{
|
||
|
|
{"caps & joints default, flat",
|
||
|
|
16.0f, 0.0f, {}, {}, {}, {},
|
||
|
|
"caps-square-joins-miter-flat.tga"},
|
||
|
|
{"caps butt, joins default, flat",
|
||
|
|
16.0f, 0.0f, {}, {},
|
||
|
|
LineCapStyle::Butt, {},
|
||
|
|
"caps-butt-joins-miter-flat.tga"},
|
||
|
|
{"caps butt, joins bevel", /** @todo bevel smoothing is off for 30° */
|
||
|
|
16.0f, 1.0f, {}, {},
|
||
|
|
LineCapStyle::Butt, LineJoinStyle::Bevel,
|
||
|
|
"caps-butt-joins-bevel.tga"},
|
||
|
|
{"caps square, joins miter",
|
||
|
|
16.0f, 1.0f, {}, {},
|
||
|
|
LineCapStyle::Square, LineJoinStyle::Miter,
|
||
|
|
"caps-square-joins-miter.tga"},
|
||
|
|
{"caps square, joins bevel", /** @todo bevel smoothing is off for 30° */
|
||
|
|
16.0f, 1.0f, {}, {},
|
||
|
|
LineCapStyle::Square, LineJoinStyle::Bevel,
|
||
|
|
"caps-square-joins-bevel.tga"},
|
||
|
|
{"caps square, joins miter, limit 3.95",
|
||
|
|
16.0f, 1.0f, 3.95f, {},
|
||
|
|
LineCapStyle::Square, LineJoinStyle::Miter,
|
||
|
|
/* Same as default */
|
||
|
|
"caps-square-joins-miter.tga"},
|
||
|
|
{"caps square, joins miter, limit 3.6",
|
||
|
|
16.0f, 1.0f, 3.6f, {}, /** @todo 3.85 should work but it doesn't */
|
||
|
|
LineCapStyle::Square, LineJoinStyle::Miter,
|
||
|
|
/* The 30° join should get a bevel here */
|
||
|
|
"caps-square-joins-miter-limit-36.tga"},
|
||
|
|
{"caps square, joins miter, limit 59°",
|
||
|
|
16.0f, 1.0f, {}, 59.0_degf,
|
||
|
|
LineCapStyle::Square, LineJoinStyle::Miter,
|
||
|
|
/* Same as limit 3.6, the 30° join gets a bevel */
|
||
|
|
"caps-square-joins-miter-limit-36.tga"},
|
||
|
|
{"caps square, joins miter, limit 70°",
|
||
|
|
16.0f, 1.0f, {}, 70.0_degf, /** @todo 61° should work but it doesn't */
|
||
|
|
LineCapStyle::Square, LineJoinStyle::Miter,
|
||
|
|
/* The 30° and 60° join should get a bevel here, 90° and 120° should
|
||
|
|
stay */
|
||
|
|
"caps-square-joins-miter-limit-70deg.tga"},
|
||
|
|
{"caps square, joins miter, limit 89°",
|
||
|
|
16.0f, 1.0f, {}, 89.0_degf,
|
||
|
|
LineCapStyle::Square, LineJoinStyle::Miter,
|
||
|
|
/* Same as limit 61°, the 30° and 60° joins get a bevel, 90° and 120°
|
||
|
|
not */
|
||
|
|
"caps-square-joins-miter-limit-70deg.tga"},
|
||
|
|
{"caps square, joins miter, limit 91°",
|
||
|
|
16.0f, 1.0f, {}, 91.0_degf,
|
||
|
|
LineCapStyle::Square, LineJoinStyle::Miter,
|
||
|
|
/* The 30°, 60° and 90° join should get a bevel here, 120° should
|
||
|
|
stay */
|
||
|
|
"caps-square-joins-miter-limit-91deg.tga"},
|
||
|
|
{"caps round, joins miter",
|
||
|
|
/** @todo use round joins instead once implemented */
|
||
|
|
16.0f, 1.0f, {}, {},
|
||
|
|
LineCapStyle::Round, LineJoinStyle::Miter,
|
||
|
|
"caps-round-joins-miter.tga"},
|
||
|
|
{"caps triangle, joins bevel",
|
||
|
|
16.0f, 1.0f, {}, {},
|
||
|
|
LineCapStyle::Triangle, LineJoinStyle::Bevel,
|
||
|
|
"caps-triangle-joins-bevel.tga"},
|
||
|
|
};
|
||
|
|
|
||
|
|
const struct {
|
||
|
|
const char* name;
|
||
|
|
Float width;
|
||
|
|
Float smoothness;
|
||
|
|
Containers::Optional<Float> miterLengthLimit;
|
||
|
|
Containers::Optional<LineCapStyle> capStyle;
|
||
|
|
Containers::Optional<LineJoinStyle> joinStyle;
|
||
|
|
bool renderSolidCube;
|
||
|
|
const char* expected;
|
||
|
|
} RenderCube3DData[]{
|
||
|
|
{"caps & joins default, flat, single-pixel",
|
||
|
|
1.0f, 0.0f, 8.0f, {}, {}, false,
|
||
|
|
"cube3D-flat-single-pixel.tga"},
|
||
|
|
{"caps square, joins miter",
|
||
|
|
10.0f, 1.0f, 8.0f, {}, {}, false,
|
||
|
|
"cube3D-caps-square-joins-miter.tga"},
|
||
|
|
{"caps butt, joins bevel", /** @todo bevel smoothing is off */
|
||
|
|
10.0f, 1.0f, {}, LineCapStyle::Butt, LineJoinStyle::Bevel, false,
|
||
|
|
"cube3D-caps-butt-joins-bevel.tga"},
|
||
|
|
{"depth", /** @todo depth is imprecise for wide lines */
|
||
|
|
/* Not smooth, as the cut-off pieces are jaggy anyway */
|
||
|
|
10.0f, 0.0f, 8.0f, {}, {}, true,
|
||
|
|
"cube3D-depth.tga"},
|
||
|
|
};
|
||
|
|
|
||
|
|
const struct {
|
||
|
|
const char* name;
|
||
|
|
Float width;
|
||
|
|
Float smoothness;
|
||
|
|
LineCapStyle capStyle;
|
||
|
|
} RenderObjectIdData[]{
|
||
|
|
{"flat, square caps", 10.0f, 0.0f, LineCapStyle::Square},
|
||
|
|
{"flat, round caps", 10.0f, 0.0f, LineCapStyle::Round},
|
||
|
|
{"smooth, square caps", 4.0f, 3.0f, LineCapStyle::Square},
|
||
|
|
};
|
||
|
|
|
||
|
|
const struct {
|
||
|
|
const char* name;
|
||
|
|
UnsignedInt expectedId[3];
|
||
|
|
LineGL2D::Flags flags;
|
||
|
|
} RenderInstancedData[]{
|
||
|
|
{"",
|
||
|
|
{}, {}},
|
||
|
|
{"object ID",
|
||
|
|
{1000, 1000, 1000}, LineGL2D::Flag::ObjectId},
|
||
|
|
{"instanced object ID",
|
||
|
|
{1211, 5627, 36363}, LineGL2D::Flag::InstancedObjectId},
|
||
|
|
};
|
||
|
|
|
||
|
|
const struct {
|
||
|
|
const char* name;
|
||
|
|
UnsignedInt expectedId[3];
|
||
|
|
LineGL2D::Flags flags;
|
||
|
|
UnsignedInt materialCount, drawCount;
|
||
|
|
UnsignedInt uniformIncrement;
|
||
|
|
} RenderMultiData[]{
|
||
|
|
{"bind with offset",
|
||
|
|
{}, {}, 1, 1, 16},
|
||
|
|
{"bind with offset, object ID",
|
||
|
|
{1211, 5627, 36363}, LineGL2D::Flag::ObjectId, 1, 1, 16},
|
||
|
|
{"draw offset",
|
||
|
|
{}, {}, 2, 3, 1},
|
||
|
|
{"draw offset, object ID",
|
||
|
|
{1211, 5627, 36363}, LineGL2D::Flag::ObjectId, 2, 3, 1},
|
||
|
|
{"multidraw",
|
||
|
|
{}, LineGL2D::Flag::MultiDraw, 2, 3, 1},
|
||
|
|
{"multidraw, object ID",
|
||
|
|
{1211, 5627, 36363}, LineGL2D::Flag::MultiDraw|LineGL2D::Flag::ObjectId, 2, 3, 1},
|
||
|
|
};
|
||
|
|
|
||
|
|
LineGLTest::LineGLTest() {
|
||
|
|
addInstancedTests<LineGLTest>({
|
||
|
|
&LineGLTest::construct<2>,
|
||
|
|
&LineGLTest::construct<3>},
|
||
|
|
Containers::arraySize(ConstructData));
|
||
|
|
|
||
|
|
addTests<LineGLTest>({
|
||
|
|
&LineGLTest::constructAsync<2>,
|
||
|
|
&LineGLTest::constructAsync<3>});
|
||
|
|
|
||
|
|
addInstancedTests<LineGLTest>({
|
||
|
|
&LineGLTest::constructUniformBuffers<2>,
|
||
|
|
&LineGLTest::constructUniformBuffers<3>},
|
||
|
|
Containers::arraySize(ConstructUniformBuffersData));
|
||
|
|
|
||
|
|
addTests<LineGLTest>({
|
||
|
|
&LineGLTest::constructUniformBuffersAsync<2>,
|
||
|
|
&LineGLTest::constructUniformBuffersAsync<3>,
|
||
|
|
|
||
|
|
&LineGLTest::constructMove<2>,
|
||
|
|
&LineGLTest::constructMove<3>,
|
||
|
|
|
||
|
|
&LineGLTest::constructMoveUniformBuffers<2>,
|
||
|
|
&LineGLTest::constructMoveUniformBuffers<3>});
|
||
|
|
|
||
|
|
addInstancedTests<LineGLTest>({
|
||
|
|
&LineGLTest::constructUniformBuffersInvalid<2>,
|
||
|
|
&LineGLTest::constructUniformBuffersInvalid<3>},
|
||
|
|
Containers::arraySize(ConstructUniformBuffersInvalidData));
|
||
|
|
|
||
|
|
addTests<LineGLTest>({
|
||
|
|
&LineGLTest::setUniformUniformBuffersEnabled<2>,
|
||
|
|
&LineGLTest::setUniformUniformBuffersEnabled<3>,
|
||
|
|
&LineGLTest::bindBufferUniformBuffersNotEnabled<2>,
|
||
|
|
&LineGLTest::bindBufferUniformBuffersNotEnabled<3>});
|
||
|
|
|
||
|
|
addInstancedTests<LineGLTest>({
|
||
|
|
&LineGLTest::setMiterLengthLimitInvalid<2>,
|
||
|
|
&LineGLTest::setMiterLengthLimitInvalid<3>},
|
||
|
|
Containers::arraySize(SetMiterLengthLimitInvalidData));
|
||
|
|
|
||
|
|
addInstancedTests<LineGLTest>({
|
||
|
|
&LineGLTest::setMiterAngleLimitInvalid<2>,
|
||
|
|
&LineGLTest::setMiterAngleLimitInvalid<3>},
|
||
|
|
Containers::arraySize(SetMiterAngleLimitInvalidData));
|
||
|
|
|
||
|
|
addTests<LineGLTest>({
|
||
|
|
&LineGLTest::setObjectIdNotEnabled<2>,
|
||
|
|
&LineGLTest::setObjectIdNotEnabled<3>,
|
||
|
|
&LineGLTest::setWrongDrawOffset<2>,
|
||
|
|
&LineGLTest::setWrongDrawOffset<3>});
|
||
|
|
|
||
|
|
/* MSVC needs explicit type due to default template args */
|
||
|
|
addTests<LineGLTest>({
|
||
|
|
&LineGLTest::renderDefaults2D,
|
||
|
|
&LineGLTest::renderDefaults2D<LineGL2D::Flag::UniformBuffers>,
|
||
|
|
&LineGLTest::renderDefaults3D,
|
||
|
|
&LineGLTest::renderDefaults3D<LineGL3D::Flag::UniformBuffers>},
|
||
|
|
&LineGLTest::renderSetupSmall,
|
||
|
|
&LineGLTest::renderTeardown);
|
||
|
|
|
||
|
|
/* MSVC needs explicit type due to default template args */
|
||
|
|
addInstancedTests<LineGLTest>({
|
||
|
|
&LineGLTest::renderLineCapsJoins2D,
|
||
|
|
&LineGLTest::renderLineCapsJoins2D<LineGL2D::Flag::UniformBuffers>,
|
||
|
|
&LineGLTest::renderLineCapsJoins2DReversed,
|
||
|
|
&LineGLTest::renderLineCapsJoins2DTransformed},
|
||
|
|
Containers::arraySize(RenderLineCapsJoins2DData),
|
||
|
|
&LineGLTest::renderSetupLarge,
|
||
|
|
&LineGLTest::renderTeardown);
|
||
|
|
|
||
|
|
/* MSVC needs explicit type due to default template args */
|
||
|
|
addInstancedTests<LineGLTest>({
|
||
|
|
&LineGLTest::renderCube3D,
|
||
|
|
&LineGLTest::renderCube3D<LineGL3D::Flag::UniformBuffers>},
|
||
|
|
Containers::arraySize(RenderCube3DData),
|
||
|
|
&LineGLTest::renderSetupLarge,
|
||
|
|
&LineGLTest::renderTeardown);
|
||
|
|
|
||
|
|
addTests({&LineGLTest::renderPerspective3D},
|
||
|
|
&LineGLTest::renderSetupSmall,
|
||
|
|
&LineGLTest::renderTeardown);
|
||
|
|
|
||
|
|
/* MSVC needs explicit type due to default template args */
|
||
|
|
addTests<LineGLTest>({
|
||
|
|
&LineGLTest::renderVertexColor2D<Color3>,
|
||
|
|
&LineGLTest::renderVertexColor2D<Color3, LineGL2D::Flag::UniformBuffers>,
|
||
|
|
&LineGLTest::renderVertexColor2D<Color4>,
|
||
|
|
&LineGLTest::renderVertexColor2D<Color4, LineGL2D::Flag::UniformBuffers>,
|
||
|
|
&LineGLTest::renderVertexColor3D<Color3>,
|
||
|
|
&LineGLTest::renderVertexColor3D<Color3, LineGL3D::Flag::UniformBuffers>,
|
||
|
|
&LineGLTest::renderVertexColor3D<Color4>,
|
||
|
|
&LineGLTest::renderVertexColor3D<Color4, LineGL3D::Flag::UniformBuffers>},
|
||
|
|
&LineGLTest::renderSetupSmall,
|
||
|
|
&LineGLTest::renderTeardown);
|
||
|
|
|
||
|
|
/* MSVC needs explicit type due to default template args */
|
||
|
|
addInstancedTests<LineGLTest>({
|
||
|
|
&LineGLTest::renderObjectId2D,
|
||
|
|
&LineGLTest::renderObjectId2D<LineGL2D::Flag::UniformBuffers>,
|
||
|
|
&LineGLTest::renderObjectId3D,
|
||
|
|
&LineGLTest::renderObjectId3D<LineGL3D::Flag::UniformBuffers>},
|
||
|
|
Containers::arraySize(RenderObjectIdData),
|
||
|
|
&LineGLTest::renderSetupSmall,
|
||
|
|
&LineGLTest::renderTeardown);
|
||
|
|
|
||
|
|
/* MSVC needs explicit type due to default template args */
|
||
|
|
addInstancedTests<LineGLTest>({
|
||
|
|
&LineGLTest::renderInstanced2D,
|
||
|
|
&LineGLTest::renderInstanced2D<LineGL2D::Flag::UniformBuffers>,
|
||
|
|
&LineGLTest::renderInstanced3D,
|
||
|
|
&LineGLTest::renderInstanced3D<LineGL2D::Flag::UniformBuffers>},
|
||
|
|
Containers::arraySize(RenderInstancedData),
|
||
|
|
&LineGLTest::renderSetupSmall,
|
||
|
|
&LineGLTest::renderTeardown);
|
||
|
|
|
||
|
|
addInstancedTests({&LineGLTest::renderMulti2D,
|
||
|
|
&LineGLTest::renderMulti3D},
|
||
|
|
Containers::arraySize(RenderMultiData),
|
||
|
|
&LineGLTest::renderSetupSmall,
|
||
|
|
&LineGLTest::renderTeardown);
|
||
|
|
|
||
|
|
/* Load the plugins directly from the build tree. Otherwise they're either
|
||
|
|
static and already loaded or not present in the build tree */
|
||
|
|
#ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME
|
||
|
|
CORRADE_INTERNAL_ASSERT_OUTPUT(_manager.load(ANYIMAGEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded);
|
||
|
|
#endif
|
||
|
|
#ifdef TGAIMPORTER_PLUGIN_FILENAME
|
||
|
|
CORRADE_INTERNAL_ASSERT_OUTPUT(_manager.load(TGAIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void LineGLTest::construct() {
|
||
|
|
auto&& data = ConstructData[testCaseInstanceId()];
|
||
|
|
setTestCaseDescription(data.name);
|
||
|
|
setTestCaseTemplateName(Utility::format("{}", dimensions));
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
typename LineGL<dimensions>::Configuration configuration;
|
||
|
|
configuration.setFlags(data.flags);
|
||
|
|
if(data.capStyle) configuration.setCapStyle(*data.capStyle);
|
||
|
|
if(data.joinStyle) configuration.setJoinStyle(*data.joinStyle);
|
||
|
|
LineGL<dimensions> shader{configuration};
|
||
|
|
CORRADE_COMPARE(shader.flags(), data.flags);
|
||
|
|
if(data.capStyle) CORRADE_COMPARE(shader.capStyle(), data.capStyle);
|
||
|
|
if(data.joinStyle) CORRADE_COMPARE(shader.joinStyle(), data.joinStyle);
|
||
|
|
CORRADE_VERIFY(shader.id());
|
||
|
|
{
|
||
|
|
#if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES)
|
||
|
|
CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly.");
|
||
|
|
#endif
|
||
|
|
CORRADE_VERIFY(shader.validate().first());
|
||
|
|
}
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
}
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void LineGLTest::constructAsync() {
|
||
|
|
setTestCaseTemplateName(Utility::format("{}", dimensions));
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
typename LineGL<dimensions>::CompileState state = LineGL<dimensions>::compile(typename LineGL<dimensions>::Configuration{}
|
||
|
|
.setFlags(LineGL2D::Flag::VertexColor)
|
||
|
|
.setCapStyle(LineCapStyle::Butt)
|
||
|
|
.setJoinStyle(LineJoinStyle::Bevel));
|
||
|
|
CORRADE_COMPARE(state.flags(), LineGL2D::Flag::VertexColor);
|
||
|
|
CORRADE_COMPARE(state.capStyle(), LineCapStyle::Butt);
|
||
|
|
CORRADE_COMPARE(state.joinStyle(), LineJoinStyle::Bevel);
|
||
|
|
|
||
|
|
while(!state.isLinkFinished())
|
||
|
|
Utility::System::sleep(100);
|
||
|
|
|
||
|
|
LineGL<dimensions> shader{std::move(state)};
|
||
|
|
CORRADE_COMPARE(shader.flags(), LineGL2D::Flag::VertexColor);
|
||
|
|
CORRADE_COMPARE(shader.capStyle(), LineCapStyle::Butt);
|
||
|
|
CORRADE_COMPARE(shader.joinStyle(), LineJoinStyle::Bevel);
|
||
|
|
|
||
|
|
CORRADE_VERIFY(shader.id());
|
||
|
|
{
|
||
|
|
#if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES)
|
||
|
|
CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly.");
|
||
|
|
#endif
|
||
|
|
CORRADE_VERIFY(shader.validate().first());
|
||
|
|
}
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
}
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void LineGLTest::constructUniformBuffers() {
|
||
|
|
auto&& data = ConstructUniformBuffersData[testCaseInstanceId()];
|
||
|
|
setTestCaseDescription(data.name);
|
||
|
|
setTestCaseTemplateName(Utility::format("{}", dimensions));
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
if((data.flags & LineGL2D::Flag::UniformBuffers) && !GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if(data.flags >= LineGL2D::Flag::MultiDraw) {
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::shader_draw_parameters>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported.");
|
||
|
|
#elif !defined(MAGNUM_TARGET_WEBGL)
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ANGLE::multi_draw>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported.");
|
||
|
|
#else
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::WEBGL::multi_draw>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
typename LineGL<dimensions>::Configuration configuration;
|
||
|
|
configuration
|
||
|
|
.setFlags(data.flags)
|
||
|
|
.setMaterialCount(data.materialCount)
|
||
|
|
.setDrawCount(data.drawCount);
|
||
|
|
if(data.capStyle) configuration.setCapStyle(*data.capStyle);
|
||
|
|
if(data.joinStyle) configuration.setJoinStyle(*data.joinStyle);
|
||
|
|
LineGL<dimensions> shader{configuration};
|
||
|
|
CORRADE_COMPARE(shader.flags(), data.flags);
|
||
|
|
if(data.capStyle) CORRADE_COMPARE(shader.capStyle(), data.capStyle);
|
||
|
|
if(data.joinStyle) CORRADE_COMPARE(shader.joinStyle(), data.joinStyle);
|
||
|
|
CORRADE_COMPARE(shader.materialCount(), data.materialCount);
|
||
|
|
CORRADE_COMPARE(shader.drawCount(), data.drawCount);
|
||
|
|
CORRADE_VERIFY(shader.id());
|
||
|
|
{
|
||
|
|
#if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES)
|
||
|
|
CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly.");
|
||
|
|
#endif
|
||
|
|
CORRADE_VERIFY(shader.validate().first());
|
||
|
|
}
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
}
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void LineGLTest::constructUniformBuffersAsync() {
|
||
|
|
setTestCaseTemplateName(Utility::format("{}", dimensions));
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
typename LineGL<dimensions>::CompileState state = LineGL<dimensions>::compile(typename LineGL<dimensions>::Configuration{}
|
||
|
|
.setFlags(LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::VertexColor)
|
||
|
|
.setCapStyle(LineCapStyle::Butt)
|
||
|
|
.setJoinStyle(LineJoinStyle::Bevel)
|
||
|
|
.setMaterialCount(16)
|
||
|
|
.setDrawCount(41));
|
||
|
|
CORRADE_COMPARE(state.flags(), LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::VertexColor);
|
||
|
|
CORRADE_COMPARE(state.capStyle(), LineCapStyle::Butt);
|
||
|
|
CORRADE_COMPARE(state.joinStyle(), LineJoinStyle::Bevel);
|
||
|
|
CORRADE_COMPARE(state.materialCount(), 16);
|
||
|
|
CORRADE_COMPARE(state.drawCount(), 41);
|
||
|
|
|
||
|
|
while(!state.isLinkFinished())
|
||
|
|
Utility::System::sleep(100);
|
||
|
|
|
||
|
|
LineGL<dimensions> shader{std::move(state)};
|
||
|
|
CORRADE_COMPARE(shader.flags(), LineGL2D::Flag::UniformBuffers|LineGL2D::Flag::VertexColor);
|
||
|
|
CORRADE_COMPARE(shader.capStyle(), LineCapStyle::Butt);
|
||
|
|
CORRADE_COMPARE(shader.joinStyle(), LineJoinStyle::Bevel);
|
||
|
|
CORRADE_COMPARE(shader.materialCount(), 16);
|
||
|
|
CORRADE_COMPARE(shader.drawCount(), 41);
|
||
|
|
CORRADE_VERIFY(shader.id());
|
||
|
|
{
|
||
|
|
#if defined(CORRADE_TARGET_APPLE) && !defined(MAGNUM_TARGET_GLES)
|
||
|
|
CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly.");
|
||
|
|
#endif
|
||
|
|
CORRADE_VERIFY(shader.validate().first());
|
||
|
|
}
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
}
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void LineGLTest::constructMove() {
|
||
|
|
setTestCaseTemplateName(Utility::format("{}", dimensions));
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LineGL<dimensions> a{typename LineGL<dimensions>::Configuration{}
|
||
|
|
.setFlags(LineGL<dimensions>::Flag::VertexColor)};
|
||
|
|
const GLuint id = a.id();
|
||
|
|
CORRADE_VERIFY(id);
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
LineGL<dimensions> b{std::move(a)};
|
||
|
|
CORRADE_COMPARE(b.id(), id);
|
||
|
|
CORRADE_COMPARE(b.flags(), LineGL<dimensions>::Flag::VertexColor);
|
||
|
|
CORRADE_VERIFY(!a.id());
|
||
|
|
|
||
|
|
LineGL<dimensions> c{NoCreate};
|
||
|
|
c = std::move(b);
|
||
|
|
CORRADE_COMPARE(c.id(), id);
|
||
|
|
CORRADE_COMPARE(c.flags(), LineGL<dimensions>::Flag::VertexColor);
|
||
|
|
CORRADE_VERIFY(!b.id());
|
||
|
|
}
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void LineGLTest::constructMoveUniformBuffers() {
|
||
|
|
setTestCaseTemplateName(Utility::format("{}", dimensions));
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LineGL<dimensions> a{typename LineGL<dimensions>::Configuration{}
|
||
|
|
.setFlags(LineGL<dimensions>::Flag::UniformBuffers)
|
||
|
|
.setMaterialCount(2)
|
||
|
|
.setDrawCount(5)};
|
||
|
|
const GLuint id = a.id();
|
||
|
|
CORRADE_VERIFY(id);
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
LineGL<dimensions> b{std::move(a)};
|
||
|
|
CORRADE_COMPARE(b.id(), id);
|
||
|
|
CORRADE_COMPARE(b.flags(), LineGL<dimensions>::Flag::UniformBuffers);
|
||
|
|
CORRADE_COMPARE(b.materialCount(), 2);
|
||
|
|
CORRADE_COMPARE(b.drawCount(), 5);
|
||
|
|
CORRADE_VERIFY(!a.id());
|
||
|
|
|
||
|
|
LineGL<dimensions> c{NoCreate};
|
||
|
|
c = std::move(b);
|
||
|
|
CORRADE_COMPARE(c.id(), id);
|
||
|
|
CORRADE_COMPARE(c.flags(), LineGL<dimensions>::Flag::UniformBuffers);
|
||
|
|
CORRADE_COMPARE(c.materialCount(), 2);
|
||
|
|
CORRADE_COMPARE(c.drawCount(), 5);
|
||
|
|
CORRADE_VERIFY(!b.id());
|
||
|
|
}
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void LineGLTest::constructUniformBuffersInvalid() {
|
||
|
|
auto&& data = ConstructUniformBuffersInvalidData[testCaseInstanceId()];
|
||
|
|
setTestCaseTemplateName(Utility::format("{}", dimensions));
|
||
|
|
setTestCaseDescription(data.name);
|
||
|
|
|
||
|
|
CORRADE_SKIP_IF_NO_ASSERT();
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
std::ostringstream out;
|
||
|
|
Error redirectError{&out};
|
||
|
|
LineGL<dimensions>{typename LineGL<dimensions>::Configuration{}
|
||
|
|
.setFlags(data.flags)
|
||
|
|
.setMaterialCount(data.materialCount)
|
||
|
|
.setDrawCount(data.drawCount)};
|
||
|
|
CORRADE_COMPARE(out.str(), Utility::formatString(
|
||
|
|
"Shaders::LineGL: {}\n", data.message));
|
||
|
|
}
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void LineGLTest::setUniformUniformBuffersEnabled() {
|
||
|
|
setTestCaseTemplateName(Utility::format("{}", dimensions));
|
||
|
|
|
||
|
|
CORRADE_SKIP_IF_NO_ASSERT();
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LineGL<dimensions> shader{typename LineGL<dimensions>::Configuration{}
|
||
|
|
.setFlags(LineGL<dimensions>::Flag::UniformBuffers)};
|
||
|
|
|
||
|
|
/* This should work fine */
|
||
|
|
shader.setViewportSize({});
|
||
|
|
|
||
|
|
std::ostringstream out;
|
||
|
|
Error redirectError{&out};
|
||
|
|
shader.setTransformationProjectionMatrix({})
|
||
|
|
.setBackgroundColor({})
|
||
|
|
.setColor({})
|
||
|
|
.setWidth({})
|
||
|
|
.setSmoothness({})
|
||
|
|
.setMiterLengthLimit({})
|
||
|
|
.setMiterAngleLimit({})
|
||
|
|
.setObjectId({});
|
||
|
|
CORRADE_COMPARE(out.str(),
|
||
|
|
"Shaders::LineGL::setTransformationProjectionMatrix(): the shader was created with uniform buffers enabled\n"
|
||
|
|
"Shaders::LineGL::setBackgroundColor(): the shader was created with uniform buffers enabled\n"
|
||
|
|
"Shaders::LineGL::setColor(): the shader was created with uniform buffers enabled\n"
|
||
|
|
"Shaders::LineGL::setWidth(): the shader was created with uniform buffers enabled\n"
|
||
|
|
"Shaders::LineGL::setSmoothness(): the shader was created with uniform buffers enabled\n"
|
||
|
|
"Shaders::LineGL::setMiterLengthLimit(): the shader was created with uniform buffers enabled\n"
|
||
|
|
"Shaders::LineGL::setMiterAngleLimit(): the shader was created with uniform buffers enabled\n"
|
||
|
|
"Shaders::LineGL::setObjectId(): the shader was created with uniform buffers enabled\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void LineGLTest::bindBufferUniformBuffersNotEnabled() {
|
||
|
|
setTestCaseTemplateName(Utility::format("{}", dimensions));
|
||
|
|
|
||
|
|
CORRADE_SKIP_IF_NO_ASSERT();
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
GL::Buffer buffer;
|
||
|
|
LineGL<dimensions> shader;
|
||
|
|
|
||
|
|
std::ostringstream out;
|
||
|
|
Error redirectError{&out};
|
||
|
|
shader.bindTransformationProjectionBuffer(buffer)
|
||
|
|
.bindTransformationProjectionBuffer(buffer, 0, 16)
|
||
|
|
.bindDrawBuffer(buffer)
|
||
|
|
.bindDrawBuffer(buffer, 0, 16)
|
||
|
|
.bindMaterialBuffer(buffer)
|
||
|
|
.bindMaterialBuffer(buffer, 0, 16)
|
||
|
|
.setDrawOffset(0);
|
||
|
|
CORRADE_COMPARE(out.str(),
|
||
|
|
"Shaders::LineGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n"
|
||
|
|
"Shaders::LineGL::bindTransformationProjectionBuffer(): the shader was not created with uniform buffers enabled\n"
|
||
|
|
"Shaders::LineGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n"
|
||
|
|
"Shaders::LineGL::bindDrawBuffer(): the shader was not created with uniform buffers enabled\n"
|
||
|
|
"Shaders::LineGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n"
|
||
|
|
"Shaders::LineGL::bindMaterialBuffer(): the shader was not created with uniform buffers enabled\n"
|
||
|
|
"Shaders::LineGL::setDrawOffset(): the shader was not created with uniform buffers enabled\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void LineGLTest::setMiterLengthLimitInvalid() {
|
||
|
|
auto&& data = SetMiterLengthLimitInvalidData[testCaseInstanceId()];
|
||
|
|
setTestCaseDescription(data.name);
|
||
|
|
setTestCaseTemplateName(Utility::format("{}", dimensions));
|
||
|
|
|
||
|
|
CORRADE_SKIP_IF_NO_ASSERT();
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
GL::Buffer buffer;
|
||
|
|
LineGL<dimensions> shader{typename LineGL<dimensions>::Configuration{}
|
||
|
|
.setJoinStyle(data.joinStyle)
|
||
|
|
};
|
||
|
|
|
||
|
|
std::ostringstream out;
|
||
|
|
Error redirectError{&out};
|
||
|
|
shader.setMiterLengthLimit(data.limit);
|
||
|
|
CORRADE_COMPARE(out.str(), Utility::formatString(
|
||
|
|
"Shaders::LineGL::setMiterLengthLimit(): {}\n", data.message));
|
||
|
|
}
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void LineGLTest::setMiterAngleLimitInvalid() {
|
||
|
|
auto&& data = SetMiterAngleLimitInvalidData[testCaseInstanceId()];
|
||
|
|
setTestCaseDescription(data.name);
|
||
|
|
setTestCaseTemplateName(Utility::format("{}", dimensions));
|
||
|
|
|
||
|
|
CORRADE_SKIP_IF_NO_ASSERT();
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
GL::Buffer buffer;
|
||
|
|
LineGL<dimensions> shader{typename LineGL<dimensions>::Configuration{}
|
||
|
|
.setJoinStyle(data.joinStyle)
|
||
|
|
};
|
||
|
|
|
||
|
|
std::ostringstream out;
|
||
|
|
Error redirectError{&out};
|
||
|
|
shader.setMiterAngleLimit(data.limit);
|
||
|
|
CORRADE_COMPARE(out.str(), Utility::formatString(
|
||
|
|
"Shaders::LineGL::setMiterAngleLimit(): {}\n", data.message));
|
||
|
|
}
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void LineGLTest::setObjectIdNotEnabled() {
|
||
|
|
setTestCaseTemplateName(Utility::format("{}", dimensions));
|
||
|
|
|
||
|
|
CORRADE_SKIP_IF_NO_ASSERT();
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LineGL<dimensions> shader;
|
||
|
|
|
||
|
|
std::ostringstream out;
|
||
|
|
Error redirectError{&out};
|
||
|
|
shader.setObjectId(33376);
|
||
|
|
CORRADE_COMPARE(out.str(),
|
||
|
|
"Shaders::LineGL::setObjectId(): the shader was not created with object ID enabled\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> void LineGLTest::setWrongDrawOffset() {
|
||
|
|
setTestCaseTemplateName(Utility::format("{}", dimensions));
|
||
|
|
|
||
|
|
CORRADE_SKIP_IF_NO_ASSERT();
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
LineGL<dimensions> shader{typename LineGL<dimensions>::Configuration{}
|
||
|
|
.setFlags(LineGL<dimensions>::Flag::UniformBuffers)
|
||
|
|
.setMaterialCount(2)
|
||
|
|
.setDrawCount(5)};
|
||
|
|
|
||
|
|
std::ostringstream out;
|
||
|
|
Error redirectError{&out};
|
||
|
|
shader.setDrawOffset(5);
|
||
|
|
CORRADE_COMPARE(out.str(),
|
||
|
|
"Shaders::LineGL::setDrawOffset(): draw offset 5 is out of bounds for 5 draws\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr Vector2i RenderSizeLarge{128, 128};
|
||
|
|
|
||
|
|
void LineGLTest::renderSetupLarge() {
|
||
|
|
/* The geometry should be generated in CCW order, enable face culling to
|
||
|
|
verify that */
|
||
|
|
GL::Renderer::enable(GL::Renderer::Feature::FaceCulling);
|
||
|
|
/* Depth test enabled only in certain cases */
|
||
|
|
|
||
|
|
_color = GL::Renderbuffer{};
|
||
|
|
_color.setStorage(GL::RenderbufferFormat::RGBA8, RenderSizeLarge);
|
||
|
|
_objectId = GL::Renderbuffer{};
|
||
|
|
_objectId.setStorage(GL::RenderbufferFormat::R32UI, RenderSizeLarge);
|
||
|
|
_depth = GL::Renderbuffer{};
|
||
|
|
_depth.setStorage(GL::RenderbufferFormat::DepthComponent24, RenderSizeLarge);
|
||
|
|
_framebuffer = GL::Framebuffer{{{}, RenderSizeLarge}};
|
||
|
|
_framebuffer
|
||
|
|
.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _color)
|
||
|
|
.attachRenderbuffer(GL::Framebuffer::ColorAttachment{1}, _objectId)
|
||
|
|
.attachRenderbuffer(GL::Framebuffer::BufferAttachment::Depth, _depth)
|
||
|
|
.mapForDraw({
|
||
|
|
{LineGL2D::ColorOutput, GL::Framebuffer::ColorAttachment{0}}
|
||
|
|
/* ObjectIdOutput mapped in test cases that actually draw to it,
|
||
|
|
otherwise it causes an error on WebGL due to the shader not
|
||
|
|
rendering to all outputs */
|
||
|
|
})
|
||
|
|
.clearDepth(1.0f)
|
||
|
|
.clearColor(0, 0x111111_rgbf)
|
||
|
|
.clearColor(1, Vector4ui{27})
|
||
|
|
.bind();
|
||
|
|
}
|
||
|
|
|
||
|
|
constexpr Vector2i RenderSizeSmall{80, 80};
|
||
|
|
|
||
|
|
void LineGLTest::renderSetupSmall() {
|
||
|
|
/* The geometry should be generated in CCW order, enable face culling to
|
||
|
|
verify that */
|
||
|
|
GL::Renderer::enable(GL::Renderer::Feature::FaceCulling);
|
||
|
|
/* Depth test enabled only in certain cases */
|
||
|
|
|
||
|
|
_color = GL::Renderbuffer{};
|
||
|
|
_color.setStorage(GL::RenderbufferFormat::RGBA8, RenderSizeSmall);
|
||
|
|
_objectId = GL::Renderbuffer{};
|
||
|
|
_objectId.setStorage(GL::RenderbufferFormat::R32UI, RenderSizeSmall);
|
||
|
|
_depth = GL::Renderbuffer{};
|
||
|
|
_depth.setStorage(GL::RenderbufferFormat::DepthComponent24, RenderSizeSmall);
|
||
|
|
_framebuffer = GL::Framebuffer{{{}, RenderSizeSmall}};
|
||
|
|
_framebuffer
|
||
|
|
.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _color)
|
||
|
|
.attachRenderbuffer(GL::Framebuffer::ColorAttachment{1}, _objectId)
|
||
|
|
.attachRenderbuffer(GL::Framebuffer::BufferAttachment::Depth, _depth)
|
||
|
|
.mapForDraw({
|
||
|
|
{LineGL2D::ColorOutput, GL::Framebuffer::ColorAttachment{0}}
|
||
|
|
/* ObjectIdOutput is mapped (and cleared) in test cases that
|
||
|
|
actually draw to it, otherwise it causes an error on WebGL due
|
||
|
|
to the shader not rendering to all outputs */
|
||
|
|
})
|
||
|
|
.clearDepth(1.0f)
|
||
|
|
.clearColor(0, 0x111111_rgbf)
|
||
|
|
.bind();
|
||
|
|
}
|
||
|
|
|
||
|
|
void LineGLTest::renderTeardown() {
|
||
|
|
_framebuffer = GL::Framebuffer{NoCreate};
|
||
|
|
_color = GL::Renderbuffer{NoCreate};
|
||
|
|
_objectId = GL::Renderbuffer{NoCreate};
|
||
|
|
_depth = GL::Renderbuffer{NoCreate};
|
||
|
|
}
|
||
|
|
|
||
|
|
/* A barebones utility for generating a line mesh. Embedded directly in the
|
||
|
|
test (as opposed to using something from MeshTools) to have it easier to
|
||
|
|
modify, debug and iterate on. */
|
||
|
|
template<UnsignedInt dimensions> struct Vertex {
|
||
|
|
VectorTypeFor<dimensions, Float> previousPosition;
|
||
|
|
VectorTypeFor<dimensions, Float> position;
|
||
|
|
VectorTypeFor<dimensions, Float> nextPosition;
|
||
|
|
LineVertexAnnotations annotation;
|
||
|
|
};
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> Containers::Array<Vertex<dimensions>> generateLineMeshVertices(Containers::StridedArrayView1D<const VectorTypeFor<dimensions, Float>> lineSegments) {
|
||
|
|
CORRADE_INTERNAL_ASSERT(lineSegments.size() % 2 == 0);
|
||
|
|
|
||
|
|
Containers::Array<Vertex<dimensions>> vertices{ValueInit, lineSegments.size()*2};
|
||
|
|
for(std::size_t i = 0; i != lineSegments.size(); ++i) {
|
||
|
|
vertices[i*2 + 0].position =
|
||
|
|
vertices[i*2 + 1].position =
|
||
|
|
lineSegments[i];
|
||
|
|
if(i % 2 == 0)
|
||
|
|
vertices[i*2 + 0].annotation =
|
||
|
|
vertices[i*2 + 1].annotation = LineVertexAnnotation::Begin;
|
||
|
|
vertices[i*2 + 0].annotation |= LineVertexAnnotation::Up;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Mark joins if the segment endpoints are the same */
|
||
|
|
for(std::size_t i = 4; i < vertices.size(); i += 4) {
|
||
|
|
if(vertices[i - 2].position != vertices[i].position)
|
||
|
|
continue;
|
||
|
|
for(std::size_t j: {i - 2, i - 1, i + 0, i + 1})
|
||
|
|
vertices[j].annotation |= LineVertexAnnotation::Join;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Prev positions for segment last vertices -- the other segment point */
|
||
|
|
for(std::size_t i = 2; i < Containers::arraySize(vertices); i += 4) {
|
||
|
|
vertices[i + 0].previousPosition =
|
||
|
|
vertices[i + 1].previousPosition =
|
||
|
|
vertices[i - 2].position;
|
||
|
|
}
|
||
|
|
/* Prev positions for segment first vertices -- a neighbor segment, if it's
|
||
|
|
a join */
|
||
|
|
for(std::size_t i = 4; i < Containers::arraySize(vertices); i += 4) {
|
||
|
|
if(!(vertices[i].annotation & LineVertexAnnotation::Join))
|
||
|
|
continue;
|
||
|
|
vertices[i + 0].previousPosition =
|
||
|
|
vertices[i + 1].previousPosition =
|
||
|
|
vertices[i - 4].position;
|
||
|
|
}
|
||
|
|
/* Next positions for segment first vertices -- the other segment point */
|
||
|
|
for(std::size_t i = 0; i < Containers::arraySize(vertices) - 2; i += 4) {
|
||
|
|
vertices[i + 0].nextPosition =
|
||
|
|
vertices[i + 1].nextPosition =
|
||
|
|
vertices[i + 2].position;
|
||
|
|
}
|
||
|
|
/* Next positions for last vertices -- a neighbor segment, if any */
|
||
|
|
for(std::size_t i = 2; i < Containers::arraySize(vertices) - 4; i += 4) {
|
||
|
|
if(!(vertices[i].annotation & LineVertexAnnotation::Join))
|
||
|
|
continue;
|
||
|
|
vertices[i + 0].nextPosition =
|
||
|
|
vertices[i + 1].nextPosition =
|
||
|
|
vertices[i + 4].position;
|
||
|
|
}
|
||
|
|
|
||
|
|
return vertices;
|
||
|
|
}
|
||
|
|
|
||
|
|
Containers::Array<UnsignedInt> generateLineMeshIndices(Containers::StridedArrayView1D<const LineVertexAnnotations> vertexAnnotations) {
|
||
|
|
Containers::Array<UnsignedInt> indices;
|
||
|
|
for(UnsignedInt i = 0; i != vertexAnnotations.size()/4; ++i) {
|
||
|
|
/* 0---2 2
|
||
|
|
| / /|
|
||
|
|
| / / |
|
||
|
|
|/ / |
|
||
|
|
1 1---3 */
|
||
|
|
arrayAppend(indices, {
|
||
|
|
i*4 + 0,
|
||
|
|
i*4 + 1,
|
||
|
|
i*4 + 2,
|
||
|
|
i*4 + 2,
|
||
|
|
i*4 + 1,
|
||
|
|
i*4 + 3
|
||
|
|
});
|
||
|
|
|
||
|
|
/* Add also indices for the bevel in both orientations (one will always
|
||
|
|
degenerate)
|
||
|
|
|
||
|
|
2 2 2---4 4 4--
|
||
|
|
/| | / /| |
|
||
|
|
/ | | / / | | /
|
||
|
|
| |/ / | |/
|
||
|
|
--3 3 3---5 5 5 */
|
||
|
|
if(vertexAnnotations[i*4 + 3] & LineVertexAnnotation::Join) {
|
||
|
|
arrayAppend(indices, {
|
||
|
|
i*4 + 2,
|
||
|
|
i*4 + 3,
|
||
|
|
i*4 + 4,
|
||
|
|
i*4 + 4,
|
||
|
|
i*4 + 3,
|
||
|
|
i*4 + 5
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return indices;
|
||
|
|
}
|
||
|
|
|
||
|
|
template<UnsignedInt dimensions> GL::Mesh generateLineMesh(Containers::StridedArrayView1D<const VectorTypeFor<dimensions, Float>> lineSegments) {
|
||
|
|
Containers::Array<Vertex<dimensions>> vertices = generateLineMeshVertices<dimensions>(lineSegments);
|
||
|
|
Containers::Array<UnsignedInt> indices = generateLineMeshIndices(stridedArrayView(vertices).slice(&Vertex<dimensions>::annotation));
|
||
|
|
|
||
|
|
GL::Mesh mesh;
|
||
|
|
mesh.addVertexBuffer(GL::Buffer{GL::Buffer::TargetHint::Array, vertices}, 0,
|
||
|
|
typename LineGL<dimensions>::PreviousPosition{},
|
||
|
|
typename LineGL<dimensions>::Position{},
|
||
|
|
typename LineGL<dimensions>::NextPosition{},
|
||
|
|
typename LineGL<dimensions>::Annotation{})
|
||
|
|
.setIndexBuffer(GL::Buffer{GL::Buffer::TargetHint::ElementArray, indices}, 0, GL::MeshIndexType::UnsignedInt)
|
||
|
|
.setCount(indices.size());
|
||
|
|
|
||
|
|
return mesh;
|
||
|
|
}
|
||
|
|
|
||
|
|
GL::Mesh generateLineMesh(std::initializer_list<Vector2> lineSegments) {
|
||
|
|
return generateLineMesh<2>(Containers::arrayView(lineSegments));
|
||
|
|
}
|
||
|
|
|
||
|
|
GL::Mesh generateLineMesh(std::initializer_list<Vector3> lineSegments) {
|
||
|
|
return generateLineMesh<3>(Containers::arrayView(lineSegments));
|
||
|
|
}
|
||
|
|
|
||
|
|
template<LineGL2D::Flag flag> void LineGLTest::renderDefaults2D() {
|
||
|
|
if(flag == LineGL2D::Flag::UniformBuffers) {
|
||
|
|
setTestCaseTemplateName("Flag::UniformBuffers");
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
GL::Mesh lines = generateLineMesh({
|
||
|
|
/* A / line from the top to bottom */
|
||
|
|
{-0.0f, 0.5f}, {-0.5f, -0.5f},
|
||
|
|
/* A / line from the bottom to top */
|
||
|
|
{-0.5f, -0.5f}, {0.5f, -0.25f},
|
||
|
|
/* A | line from the bottom to top */
|
||
|
|
{-0.75f, -0.25f}, {-0.75f, 0.75f},
|
||
|
|
/* A _ line from the left to right */
|
||
|
|
{-0.25f, -0.75f}, {0.75f, -0.75f},
|
||
|
|
/* A zero-size line that should be visible as a point */
|
||
|
|
{0.5f, 0.5f}, {0.5f, 0.5f}
|
||
|
|
});
|
||
|
|
|
||
|
|
LineGL2D shader{LineGL2D::Configuration{}
|
||
|
|
.setFlags(flag)};
|
||
|
|
shader.setViewportSize(Vector2{RenderSizeSmall});
|
||
|
|
|
||
|
|
/* Enabling blending and a half-transparent color -- there should be no
|
||
|
|
overlaps */
|
||
|
|
GL::Renderer::enable(GL::Renderer::Feature::Blending);
|
||
|
|
GL::Renderer::setBlendFunction(
|
||
|
|
GL::Renderer::BlendFunction::One,
|
||
|
|
GL::Renderer::BlendFunction::OneMinusSourceAlpha);
|
||
|
|
|
||
|
|
if(flag == LineGL2D::Flag{}) {
|
||
|
|
shader.draw(lines);
|
||
|
|
} else if(flag == LineGL2D::Flag::UniformBuffers) {
|
||
|
|
GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
TransformationProjectionUniform2D{}
|
||
|
|
}};
|
||
|
|
GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineDrawUniform{}
|
||
|
|
}};
|
||
|
|
GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineMaterialUniform{}
|
||
|
|
}};
|
||
|
|
shader
|
||
|
|
.bindTransformationProjectionBuffer(transformationProjectionUniform)
|
||
|
|
.bindDrawBuffer(drawUniform)
|
||
|
|
.bindMaterialBuffer(materialUniform)
|
||
|
|
.draw(lines);
|
||
|
|
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE();
|
||
|
|
|
||
|
|
GL::Renderer::disable(GL::Renderer::Feature::Blending);
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
|
||
|
|
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
|
||
|
|
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
|
||
|
|
|
||
|
|
CORRADE_COMPARE_WITH(
|
||
|
|
/* Dropping the alpha channel, as it's always 1.0 */
|
||
|
|
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
|
||
|
|
Utility::Path::join(SHADERS_TEST_DIR, "LineTestFiles/defaults.tga"),
|
||
|
|
(DebugTools::CompareImageToFile{_manager}));
|
||
|
|
}
|
||
|
|
|
||
|
|
template<LineGL3D::Flag flag> void LineGLTest::renderDefaults3D() {
|
||
|
|
if(flag == LineGL3D::Flag::UniformBuffers) {
|
||
|
|
setTestCaseTemplateName("Flag::UniformBuffers");
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* Same as in the 2D case, just with a varying Z coordinate added. As the
|
||
|
|
implicit projection is orthographic, this should result in the exact
|
||
|
|
same output as 2D. */
|
||
|
|
GL::Mesh lines = generateLineMesh({
|
||
|
|
/* A / line from the top to bottom, front to back */
|
||
|
|
{-0.0f, 0.5f, 1.0f}, {-0.5f, -0.5f, -1.0f},
|
||
|
|
/* A / line from the bottom to top, back to front */
|
||
|
|
{-0.5f, -0.5f, -1.0f}, {0.5f, -0.25f, 1.0f},
|
||
|
|
/* A | line from the bottom to top, on the back */
|
||
|
|
{-0.75f, -0.25f, -1.0f}, {-0.75f, 0.75f, -1.0f},
|
||
|
|
/* A _ line from the left to right, on the front */
|
||
|
|
{-0.25f, -0.75f, 1.0f}, {0.75f, -0.75f, 1.0f},
|
||
|
|
/* A zero-size line that should be visible as a point, in the middle */
|
||
|
|
{0.5f, 0.5f, 0.0f}, {0.5f, 0.5f, 0.0f}
|
||
|
|
});
|
||
|
|
|
||
|
|
LineGL3D shader{LineGL3D::Configuration{}
|
||
|
|
.setFlags(flag)};
|
||
|
|
shader.setViewportSize(Vector2{RenderSizeSmall});
|
||
|
|
|
||
|
|
/* Enabling blending and a half-transparent color -- there should be no
|
||
|
|
overlaps */
|
||
|
|
GL::Renderer::enable(GL::Renderer::Feature::Blending);
|
||
|
|
GL::Renderer::setBlendFunction(
|
||
|
|
GL::Renderer::BlendFunction::One,
|
||
|
|
GL::Renderer::BlendFunction::OneMinusSourceAlpha);
|
||
|
|
|
||
|
|
if(flag == LineGL3D::Flag{}) {
|
||
|
|
shader.draw(lines);
|
||
|
|
} else if(flag == LineGL3D::Flag::UniformBuffers) {
|
||
|
|
GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
TransformationProjectionUniform3D{}
|
||
|
|
}};
|
||
|
|
GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineDrawUniform{}
|
||
|
|
}};
|
||
|
|
GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineMaterialUniform{}
|
||
|
|
}};
|
||
|
|
shader
|
||
|
|
.bindTransformationProjectionBuffer(transformationProjectionUniform)
|
||
|
|
.bindDrawBuffer(drawUniform)
|
||
|
|
.bindMaterialBuffer(materialUniform)
|
||
|
|
.draw(lines);
|
||
|
|
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE();
|
||
|
|
|
||
|
|
GL::Renderer::disable(GL::Renderer::Feature::Blending);
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
|
||
|
|
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
|
||
|
|
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
|
||
|
|
|
||
|
|
CORRADE_COMPARE_WITH(
|
||
|
|
/* Dropping the alpha channel, as it's always 1.0 */
|
||
|
|
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
|
||
|
|
Utility::Path::join(SHADERS_TEST_DIR, "LineTestFiles/defaults.tga"),
|
||
|
|
(DebugTools::CompareImageToFile{_manager}));
|
||
|
|
}
|
||
|
|
|
||
|
|
const Vector2 RenderLineCapsJoins2DLineData[]{
|
||
|
|
/* A single point (a zero-length line) */
|
||
|
|
{0.2f, 0.8f}, {0.2f, 0.8f},
|
||
|
|
/* A rotated point (i.e., a line of a very small length). Should ideally be
|
||
|
|
symmetric. */
|
||
|
|
/** @todo it isn't, why? */
|
||
|
|
{-0.4f, 0.15f}, {-0.4f + Math::TypeTraits<Float>::epsilon(), 0.15f + Math::TypeTraits<Float>::epsilon()},
|
||
|
|
/* A 90° join with a large length ratio. Caps should look the same on both
|
||
|
|
ends, independently on the length */
|
||
|
|
{-0.8f, 0.7f}, {-0.8f, -0.25f},
|
||
|
|
{-0.8f, -0.25f}, {-0.6f, -0.25f},
|
||
|
|
/* A four-segment line with a 60°, 120° and a 30° join. All should be
|
||
|
|
miters in the default setup. */
|
||
|
|
Vector2{0.25f, 0.3f} + Complex::rotation(-60.0_degf).transformVector(Vector2::yAxis(0.6f)) + Vector2::yAxis(-0.3f),
|
||
|
|
Vector2{0.25f, 0.3f} + Complex::rotation(-60.0_degf).transformVector(Vector2::yAxis(0.6f)),
|
||
|
|
Vector2{0.25f, 0.3f} + Complex::rotation(-60.0_degf).transformVector(Vector2::yAxis(0.6f)),
|
||
|
|
{0.2f, 0.35f},
|
||
|
|
{0.2f, 0.35f},
|
||
|
|
Vector2{0.25f, 0.3f} + Complex::rotation(60.0_degf).transformVector(Vector2::yAxis(0.6f)),
|
||
|
|
Vector2{0.25f, 0.3f} + Complex::rotation(60.0_degf).transformVector(Vector2::yAxis(0.6f)),
|
||
|
|
Vector2{0.25f, 0.3f} + Complex::rotation(60.0_degf).transformVector(Vector2::yAxis(0.6f)) +
|
||
|
|
Complex::rotation(30.0_degf).transformVector(Vector2::yAxis(-0.5f)),
|
||
|
|
/* A completely ordinary line segment, to test the case when everything
|
||
|
|
goes wrong */
|
||
|
|
{0.4f, -0.05f}, {0.8f, -0.05f},
|
||
|
|
/* A 180° join, with one part shorter. Should be always beveled, should not
|
||
|
|
overlap and should not disappear. */
|
||
|
|
/** @todo the other end disappears, fix */
|
||
|
|
{0.8f, -0.4f}, {0.0f, -0.4f},
|
||
|
|
{0.0f, -0.4f}, {0.8f, -0.40001f}, /** @todo otherwise disappears, fix */
|
||
|
|
/* A join where the other line touches the edge (either slightly above for
|
||
|
|
square/round caps, or slightly below for butt caps). The caps should not
|
||
|
|
get distorted in any way. */
|
||
|
|
/** @todo they do, fix */
|
||
|
|
{-0.45f, -0.8f}, {-0.7f, -0.8f},
|
||
|
|
{-0.7f, -0.8f},
|
||
|
|
Vector2{-0.7f, -0.8f} + Complex::rotation(60.0_degf).transformVector(Vector2::xAxis(0.2f)),
|
||
|
|
/* A join where the other line endpoint is inside the line. The caps should
|
||
|
|
not get distorted and it shouldn't overlap. */
|
||
|
|
/** @todo it's distorted heavily, fix */
|
||
|
|
{0.25f, -0.8f}, {0.0f, -0.8f},
|
||
|
|
{0.0f, -0.8f},
|
||
|
|
Vector2{0.0f, -0.8f} + Complex::rotation(60.0_degf).transformVector(Vector2::xAxis(0.01f)),
|
||
|
|
/* Like above, but with the first line short as well */
|
||
|
|
/** @todo here's both a heavy distortion and an overlap, fix */
|
||
|
|
{0.725f, -0.8f}, {0.7f, -0.8f},
|
||
|
|
{0.7f, -0.8f},
|
||
|
|
Vector2{0.7f, -0.8f} + Complex::rotation(60.0_degf).transformVector(Vector2::xAxis(0.01f))
|
||
|
|
};
|
||
|
|
|
||
|
|
template<LineGL2D::Flag flag> void LineGLTest::renderLineCapsJoins2D() {
|
||
|
|
auto&& data = RenderLineCapsJoins2DData[testCaseInstanceId()];
|
||
|
|
setTestCaseDescription(data.name);
|
||
|
|
|
||
|
|
if(flag == LineGL2D::Flag::UniformBuffers) {
|
||
|
|
setTestCaseTemplateName("Flag::UniformBuffers");
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
GL::Mesh lines = generateLineMesh<2>(RenderLineCapsJoins2DLineData);
|
||
|
|
|
||
|
|
LineGL2D::Configuration configuration;
|
||
|
|
configuration.setFlags(flag);
|
||
|
|
if(data.capStyle) configuration.setCapStyle(*data.capStyle);
|
||
|
|
if(data.joinStyle) configuration.setJoinStyle(*data.joinStyle);
|
||
|
|
LineGL2D shader{configuration};
|
||
|
|
shader.setViewportSize(Vector2{RenderSizeLarge});
|
||
|
|
|
||
|
|
/* Enabling blending and a half-transparent color -- there should be no
|
||
|
|
overlaps */
|
||
|
|
GL::Renderer::enable(GL::Renderer::Feature::Blending);
|
||
|
|
GL::Renderer::setBlendFunction(
|
||
|
|
GL::Renderer::BlendFunction::One,
|
||
|
|
GL::Renderer::BlendFunction::OneMinusSourceAlpha);
|
||
|
|
|
||
|
|
if(flag == LineGL2D::Flag{}) {
|
||
|
|
shader
|
||
|
|
.setWidth(data.width)
|
||
|
|
.setSmoothness(data.smoothness)
|
||
|
|
.setColor(0x80808080_rgbaf);
|
||
|
|
if(data.miterLengthLimit)
|
||
|
|
shader.setMiterLengthLimit(*data.miterLengthLimit);
|
||
|
|
if(data.miterAngleLimit)
|
||
|
|
shader.setMiterAngleLimit(*data.miterAngleLimit);
|
||
|
|
shader.draw(lines);
|
||
|
|
} else if(flag == LineGL2D::Flag::UniformBuffers) {
|
||
|
|
GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
TransformationProjectionUniform2D{}
|
||
|
|
}};
|
||
|
|
GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineDrawUniform{}
|
||
|
|
}};
|
||
|
|
|
||
|
|
LineMaterialUniform materialUniformData[1];
|
||
|
|
(*materialUniformData)
|
||
|
|
.setWidth(data.width)
|
||
|
|
.setSmoothness(data.smoothness)
|
||
|
|
.setColor(0x80808080_rgbaf);
|
||
|
|
if(data.miterLengthLimit)
|
||
|
|
materialUniformData->setMiterLengthLimit(*data.miterLengthLimit);
|
||
|
|
if(data.miterAngleLimit)
|
||
|
|
materialUniformData->setMiterAngleLimit(*data.miterAngleLimit);
|
||
|
|
GL::Buffer materialUniform{materialUniformData};
|
||
|
|
|
||
|
|
shader
|
||
|
|
.bindTransformationProjectionBuffer(transformationProjectionUniform)
|
||
|
|
.bindDrawBuffer(drawUniform)
|
||
|
|
.bindMaterialBuffer(materialUniform)
|
||
|
|
.draw(lines);
|
||
|
|
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE();
|
||
|
|
|
||
|
|
GL::Renderer::disable(GL::Renderer::Feature::Blending);
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
|
||
|
|
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
|
||
|
|
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
|
||
|
|
|
||
|
|
Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm});
|
||
|
|
|
||
|
|
CORRADE_COMPARE_WITH(
|
||
|
|
/* Dropping the alpha channel, as it's always 1.0 */
|
||
|
|
Containers::arrayCast<Color3ub>(image.pixels<Color4ub>()),
|
||
|
|
Utility::Path::join({SHADERS_TEST_DIR, "LineTestFiles", data.expected}),
|
||
|
|
(DebugTools::CompareImageToFile{_manager}));
|
||
|
|
|
||
|
|
{
|
||
|
|
/** @todo drop this when fixed */
|
||
|
|
CORRADE_EXPECT_FAIL("Rendered with overlapping geometry at the moment.");
|
||
|
|
CORRADE_COMPARE(Math::max(image.pixels<Color4ub>().asContiguous()), 0x888888ff_rgba);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void LineGLTest::renderLineCapsJoins2DReversed() {
|
||
|
|
auto&& data = RenderLineCapsJoins2DData[testCaseInstanceId()];
|
||
|
|
setTestCaseDescription(data.name);
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* As this verifies mainly the algorithm, there's no variant with UBOs --
|
||
|
|
those are sufficiently tested elsewhere */
|
||
|
|
|
||
|
|
GL::Mesh lines = generateLineMesh<2>(Containers::stridedArrayView(RenderLineCapsJoins2DLineData).flipped<0>());
|
||
|
|
|
||
|
|
/* Enabling blending and a half-transparent color -- there should be no
|
||
|
|
overlaps */
|
||
|
|
GL::Renderer::enable(GL::Renderer::Feature::Blending);
|
||
|
|
GL::Renderer::setBlendFunction(
|
||
|
|
GL::Renderer::BlendFunction::One,
|
||
|
|
GL::Renderer::BlendFunction::OneMinusSourceAlpha);
|
||
|
|
|
||
|
|
LineGL2D::Configuration configuration;
|
||
|
|
if(data.capStyle) configuration.setCapStyle(*data.capStyle);
|
||
|
|
if(data.joinStyle) configuration.setJoinStyle(*data.joinStyle);
|
||
|
|
LineGL2D shader{configuration};
|
||
|
|
shader
|
||
|
|
.setViewportSize(Vector2{RenderSizeLarge})
|
||
|
|
.setWidth(data.width)
|
||
|
|
.setSmoothness(data.smoothness)
|
||
|
|
.setColor(0x80808080_rgbaf);
|
||
|
|
if(data.miterLengthLimit) shader.setMiterLengthLimit(*data.miterLengthLimit);
|
||
|
|
if(data.miterAngleLimit) shader.setMiterAngleLimit(*data.miterAngleLimit);
|
||
|
|
|
||
|
|
/* Enabling blending and a half-transparent color -- there should be no
|
||
|
|
overlaps */
|
||
|
|
GL::Renderer::enable(GL::Renderer::Feature::Blending);
|
||
|
|
GL::Renderer::setBlendFunction(
|
||
|
|
GL::Renderer::BlendFunction::One,
|
||
|
|
GL::Renderer::BlendFunction::OneMinusSourceAlpha);
|
||
|
|
|
||
|
|
shader.draw(lines);
|
||
|
|
|
||
|
|
GL::Renderer::disable(GL::Renderer::Feature::Blending);
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
|
||
|
|
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
|
||
|
|
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
|
||
|
|
|
||
|
|
Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm});
|
||
|
|
|
||
|
|
CORRADE_COMPARE_WITH(
|
||
|
|
/* Dropping the alpha channel, as it's always 1.0 */
|
||
|
|
Containers::arrayCast<Color3ub>(image.pixels<Color4ub>()),
|
||
|
|
Utility::Path::join({SHADERS_TEST_DIR, "LineTestFiles", data.expected}),
|
||
|
|
/** @todo sync this with render2D() once the overlaps are fixed */
|
||
|
|
(DebugTools::CompareImageToFile{_manager, 1.0f, 0.0005f}));
|
||
|
|
}
|
||
|
|
|
||
|
|
void LineGLTest::renderLineCapsJoins2DTransformed() {
|
||
|
|
auto&& data = RenderLineCapsJoins2DData[testCaseInstanceId()];
|
||
|
|
setTestCaseDescription(data.name);
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* As this verifies mainly the algorithm, there's no variant with UBOs --
|
||
|
|
those are sufficiently tested elsewhere */
|
||
|
|
|
||
|
|
const Matrix3 transformation = Matrix3::scaling({100.0f, 2.0f})*Matrix3::rotation(45.0_degf);
|
||
|
|
|
||
|
|
Containers::Array<Vector2> transformedLineSegments{NoInit, Containers::arraySize(RenderLineCapsJoins2DLineData)};
|
||
|
|
for(std::size_t i = 0; i != transformedLineSegments.size(); ++i)
|
||
|
|
transformedLineSegments[i] = transformation.transformPoint(RenderLineCapsJoins2DLineData[i]);
|
||
|
|
|
||
|
|
GL::Mesh lines = generateLineMesh<2>(transformedLineSegments);
|
||
|
|
|
||
|
|
LineGL2D::Configuration configuration;
|
||
|
|
if(data.capStyle) configuration.setCapStyle(*data.capStyle);
|
||
|
|
if(data.joinStyle) configuration.setJoinStyle(*data.joinStyle);
|
||
|
|
LineGL2D shader{configuration};
|
||
|
|
shader
|
||
|
|
.setViewportSize(Vector2{RenderSizeLarge})
|
||
|
|
.setWidth(data.width)
|
||
|
|
.setSmoothness(data.smoothness)
|
||
|
|
.setTransformationProjectionMatrix(transformation.inverted())
|
||
|
|
.setColor(0x80808080_rgbaf);
|
||
|
|
|
||
|
|
if(data.miterLengthLimit) shader.setMiterLengthLimit(*data.miterLengthLimit);
|
||
|
|
if(data.miterAngleLimit) shader.setMiterAngleLimit(*data.miterAngleLimit);
|
||
|
|
|
||
|
|
/* Enabling blending and a half-transparent color -- there should be no
|
||
|
|
overlaps */
|
||
|
|
GL::Renderer::enable(GL::Renderer::Feature::Blending);
|
||
|
|
GL::Renderer::setBlendFunction(
|
||
|
|
GL::Renderer::BlendFunction::One,
|
||
|
|
GL::Renderer::BlendFunction::OneMinusSourceAlpha);
|
||
|
|
|
||
|
|
shader.draw(lines);
|
||
|
|
|
||
|
|
GL::Renderer::disable(GL::Renderer::Feature::Blending);
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
|
||
|
|
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
|
||
|
|
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
|
||
|
|
|
||
|
|
Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm});
|
||
|
|
|
||
|
|
CORRADE_COMPARE_WITH(
|
||
|
|
/* Dropping the alpha channel, as it's always 1.0 */
|
||
|
|
Containers::arrayCast<Color3ub>(image.pixels<Color4ub>()),
|
||
|
|
Utility::Path::join({SHADERS_TEST_DIR, "LineTestFiles", data.expected}),
|
||
|
|
(DebugTools::CompareImageToFile{_manager}));
|
||
|
|
}
|
||
|
|
|
||
|
|
template<LineGL3D::Flag flag> void LineGLTest::renderCube3D() {
|
||
|
|
auto&& data = RenderCube3DData[testCaseInstanceId()];
|
||
|
|
setTestCaseDescription(data.name);
|
||
|
|
|
||
|
|
if(flag == LineGL3D::Flag::UniformBuffers) {
|
||
|
|
setTestCaseTemplateName("Flag::UniformBuffers");
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* A cube, with the top and bottom quad being a loop and the sides being
|
||
|
|
disconnected segments */
|
||
|
|
Containers::Array<Vertex<3>> vertices = generateLineMeshVertices<3>(Containers::stridedArrayView<Vector3>({
|
||
|
|
{-1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, // 0 to 3, loops to 12/13
|
||
|
|
{1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, -1.0f}, // 4 to 7
|
||
|
|
{1.0f, 1.0f, -1.0f}, {-1.0f, 1.0f, -1.0f}, // 8 to 11
|
||
|
|
{-1.0f, 1.0f, -1.0f}, {-1.0f, 1.0f, 1.0f}, // 12 to 15, loops to 2/3
|
||
|
|
|
||
|
|
{-1.0f, -1.0f, 1.0f}, {1.0f, -1.0f, 1.0f}, // 16 to 19, loops to 28/29
|
||
|
|
{1.0f, -1.0f, 1.0f}, {1.0f, -1.0f, -1.0f}, // 20 to 23
|
||
|
|
{1.0f, -1.0f, -1.0f}, {-1.0f, -1.0f, -1.0f}, // 24 to 27
|
||
|
|
{-1.0f, -1.0f, -1.0f}, {-1.0f, -1.0f, 1.0f}, // 28 to 31, loops to 18/19
|
||
|
|
|
||
|
|
{-1.0f, 1.0f, 1.0f}, {-1.0f, -1.0f, 1.0f}, // 32 to 35
|
||
|
|
{1.0f, 1.0f, 1.0f}, {1.0f, -1.0f, 1.0f}, // 36 to 39
|
||
|
|
{1.0f, 1.0f, -1.0f}, {1.0f, -1.0f, -1.0f}, // 40 to 43
|
||
|
|
{-1.0f, 1.0f, -1.0f}, {-1.0f, -1.0f, -1.0f}, // 43 to 47
|
||
|
|
}));
|
||
|
|
CORRADE_COMPARE(vertices.size(), 48);
|
||
|
|
|
||
|
|
/* Check prerequisites */
|
||
|
|
for(std::size_t i: {0, 1, 16, 17}) {
|
||
|
|
CORRADE_ITERATION(i);
|
||
|
|
CORRADE_COMPARE(vertices[i].previousPosition, Vector3{});
|
||
|
|
}
|
||
|
|
for(std::size_t i: {14, 15, 30, 31}) {
|
||
|
|
CORRADE_ITERATION(i);
|
||
|
|
CORRADE_COMPARE(vertices[i].nextPosition, Vector3{});
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Manually loop the top & bottom */
|
||
|
|
vertices[0].previousPosition = vertices[12].position;
|
||
|
|
vertices[1].previousPosition = vertices[13].position;
|
||
|
|
vertices[14].nextPosition = vertices[2].position;
|
||
|
|
vertices[15].nextPosition = vertices[3].position;
|
||
|
|
|
||
|
|
vertices[16].previousPosition = vertices[28].position;
|
||
|
|
vertices[17].previousPosition = vertices[29].position;
|
||
|
|
vertices[30].nextPosition = vertices[18].position;
|
||
|
|
vertices[31].nextPosition = vertices[19].position;
|
||
|
|
|
||
|
|
Containers::Array<UnsignedInt> indices = generateLineMeshIndices(stridedArrayView(vertices).slice(&Vertex<3>::annotation));
|
||
|
|
|
||
|
|
/* Add line join annotation to the looped parts. Has to be done *after*
|
||
|
|
generating indices because otherwise it'd assume the next point of the
|
||
|
|
join is right after which it isn't. */
|
||
|
|
for(std::size_t i: {0, 1, 14, 15, 16, 17, 30, 31}) {
|
||
|
|
CORRADE_ITERATION(i);
|
||
|
|
CORRADE_VERIFY(!(vertices[i].annotation & LineVertexAnnotation::Join));
|
||
|
|
vertices[i].annotation |= LineVertexAnnotation::Join;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Add indices for the two newly created joins */
|
||
|
|
arrayAppend(indices, {
|
||
|
|
14u, 15u, 0u,
|
||
|
|
0u, 15u, 1u,
|
||
|
|
|
||
|
|
30u, 31u, 16u,
|
||
|
|
16u, 31u, 17u
|
||
|
|
});
|
||
|
|
|
||
|
|
GL::Mesh lines;
|
||
|
|
lines.addVertexBuffer(GL::Buffer{GL::Buffer::TargetHint::Array, vertices}, 0,
|
||
|
|
LineGL3D::PreviousPosition{},
|
||
|
|
LineGL3D::Position{},
|
||
|
|
LineGL3D::NextPosition{},
|
||
|
|
LineGL3D::Annotation{})
|
||
|
|
.setIndexBuffer(GL::Buffer{GL::Buffer::TargetHint::ElementArray, indices}, 0, GL::MeshIndexType::UnsignedInt)
|
||
|
|
.setCount(indices.size());
|
||
|
|
|
||
|
|
const Matrix4 projection = Matrix4::perspectiveProjection(50.0_degf, 1.0f, 0.1f, 10.0f);
|
||
|
|
const Matrix4 transformation =
|
||
|
|
Matrix4::translation({-0.125f, 0.25f, -5.0f})*
|
||
|
|
Matrix4::rotationX(25.0_degf)*
|
||
|
|
Matrix4::rotationY(30.0_degf);
|
||
|
|
|
||
|
|
if(data.renderSolidCube) {
|
||
|
|
GL::Renderer::enable(GL::Renderer::Feature::DepthTest);
|
||
|
|
|
||
|
|
PhongGL shader;
|
||
|
|
shader.setLightPositions({{-1.0f, 2.0f, 3.0f, 0.0f}})
|
||
|
|
.setProjectionMatrix(projection)
|
||
|
|
.setTransformationMatrix(transformation)
|
||
|
|
.setNormalMatrix(transformation.normalMatrix())
|
||
|
|
.setDiffuseColor(0xff0000_rgbf)
|
||
|
|
.draw(MeshTools::compile(Primitives::cubeSolid()));
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
GL::Renderer::setDepthFunction(GL::Renderer::DepthFunction::LessOrEqual);
|
||
|
|
GL::Renderer::setDepthMask(false);
|
||
|
|
}
|
||
|
|
|
||
|
|
LineGL3D::Configuration configuration;
|
||
|
|
configuration.setFlags(flag);
|
||
|
|
if(data.capStyle) configuration.setCapStyle(*data.capStyle);
|
||
|
|
if(data.joinStyle) configuration.setJoinStyle(*data.joinStyle);
|
||
|
|
LineGL3D shader{configuration};
|
||
|
|
shader.setViewportSize(Vector2{RenderSizeLarge});
|
||
|
|
|
||
|
|
/* Enabling blending and a half-transparent color -- there should be no
|
||
|
|
overlaps */
|
||
|
|
GL::Renderer::enable(GL::Renderer::Feature::Blending);
|
||
|
|
GL::Renderer::setBlendFunction(
|
||
|
|
GL::Renderer::BlendFunction::One,
|
||
|
|
GL::Renderer::BlendFunction::OneMinusSourceAlpha);
|
||
|
|
|
||
|
|
if(flag == LineGL3D::Flag{}) {
|
||
|
|
shader
|
||
|
|
.setTransformationProjectionMatrix(projection*transformation)
|
||
|
|
.setWidth(data.width)
|
||
|
|
.setSmoothness(data.smoothness)
|
||
|
|
.setColor(0x80808080_rgbaf);
|
||
|
|
if(data.miterLengthLimit)
|
||
|
|
shader.setMiterLengthLimit(*data.miterLengthLimit);
|
||
|
|
shader.draw(lines);
|
||
|
|
} else if(flag == LineGL3D::Flag::UniformBuffers) {
|
||
|
|
GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
TransformationProjectionUniform3D{}
|
||
|
|
.setTransformationProjectionMatrix(projection*transformation)
|
||
|
|
}};
|
||
|
|
GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineDrawUniform{}
|
||
|
|
}};
|
||
|
|
|
||
|
|
LineMaterialUniform materialUniformData[1];
|
||
|
|
(*materialUniformData)
|
||
|
|
.setWidth(data.width)
|
||
|
|
.setSmoothness(data.smoothness)
|
||
|
|
.setColor(0x80808080_rgbaf);
|
||
|
|
if(data.miterLengthLimit)
|
||
|
|
materialUniformData->setMiterLengthLimit(*data.miterLengthLimit);
|
||
|
|
GL::Buffer materialUniform{materialUniformData};
|
||
|
|
|
||
|
|
shader
|
||
|
|
.bindTransformationProjectionBuffer(transformationProjectionUniform)
|
||
|
|
.bindDrawBuffer(drawUniform)
|
||
|
|
.bindMaterialBuffer(materialUniform)
|
||
|
|
.draw(lines);
|
||
|
|
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE();
|
||
|
|
|
||
|
|
if(data.renderSolidCube) {
|
||
|
|
GL::Renderer::disable(GL::Renderer::Feature::DepthTest);
|
||
|
|
GL::Renderer::setDepthFunction(GL::Renderer::DepthFunction::Less);
|
||
|
|
GL::Renderer::setDepthMask(true);
|
||
|
|
}
|
||
|
|
|
||
|
|
GL::Renderer::disable(GL::Renderer::Feature::Blending);
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
|
||
|
|
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
|
||
|
|
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
|
||
|
|
|
||
|
|
Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm});
|
||
|
|
|
||
|
|
CORRADE_COMPARE_WITH(
|
||
|
|
/* Dropping the alpha channel, as it's always 1.0 */
|
||
|
|
Containers::arrayCast<Color3ub>(image.pixels<Color4ub>()),
|
||
|
|
Utility::Path::join({SHADERS_TEST_DIR, "LineTestFiles", data.expected}),
|
||
|
|
(DebugTools::CompareImageToFile{_manager}));
|
||
|
|
}
|
||
|
|
|
||
|
|
void LineGLTest::renderPerspective3D() {
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* Verify that perspective-correct interpolation isn't used (which would
|
||
|
|
cause significant artifacts) */
|
||
|
|
GL::Mesh lines = generateLineMesh({
|
||
|
|
{0.0f, -1.0f, 10.0f}, {0.0f, 7.5f, -10.0f}
|
||
|
|
});
|
||
|
|
|
||
|
|
LineGL3D shader;
|
||
|
|
shader.setViewportSize(Vector2{RenderSizeSmall})
|
||
|
|
.setTransformationProjectionMatrix(
|
||
|
|
Matrix4::perspectiveProjection(50.0_degf, 1.0f, 0.1f, 50.0f)*
|
||
|
|
Matrix4::translation({0.0f, 0.0f, -13.0f}))
|
||
|
|
.setWidth(10.0f)
|
||
|
|
.setSmoothness(1.0f)
|
||
|
|
.draw(lines);
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
|
||
|
|
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
|
||
|
|
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
|
||
|
|
|
||
|
|
CORRADE_COMPARE_WITH(
|
||
|
|
/* Dropping the alpha channel, as it's always 1.0 */
|
||
|
|
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
|
||
|
|
Utility::Path::join(SHADERS_TEST_DIR, "LineTestFiles/perspective3D.tga"),
|
||
|
|
(DebugTools::CompareImageToFile{_manager}));
|
||
|
|
}
|
||
|
|
|
||
|
|
template<class T, LineGL2D::Flag flag> void LineGLTest::renderVertexColor2D() {
|
||
|
|
if(flag == LineGL2D::Flag::UniformBuffers) {
|
||
|
|
setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"});
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
} else {
|
||
|
|
setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4");
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
GL::Mesh lines = generateLineMesh({
|
||
|
|
{-0.8f, 0.5f}, {-0.5f, -0.5f},
|
||
|
|
{-0.5f, -0.5f}, {0.0f, 0.0f},
|
||
|
|
{0.0f, 0.0f}, {0.5f, -0.5f},
|
||
|
|
{0.5f, -0.5f}, {0.8f, 0.5f}
|
||
|
|
});
|
||
|
|
|
||
|
|
/* Each line segment from above is four points */
|
||
|
|
T colors[]{
|
||
|
|
0xff0000_rgbf, 0xff0000_rgbf, 0xffff00_rgbf, 0xffff00_rgbf,
|
||
|
|
0xffff00_rgbf, 0xffff00_rgbf, 0x00ffff_rgbf, 0x00ffff_rgbf,
|
||
|
|
0x00ffff_rgbf, 0x00ffff_rgbf, 0x00ff00_rgbf, 0x00ff00_rgbf,
|
||
|
|
0x00ff00_rgbf, 0x00ff00_rgbf, 0x0000ff_rgbf, 0x0000ff_rgbf
|
||
|
|
};
|
||
|
|
if(std::is_same<T, Color3>::value)
|
||
|
|
lines.addVertexBuffer(GL::Buffer{GL::Buffer::TargetHint::Array, colors}, 0, LineGL2D::Color3{});
|
||
|
|
else
|
||
|
|
lines.addVertexBuffer(GL::Buffer{GL::Buffer::TargetHint::Array, colors}, 0, LineGL2D::Color4{});
|
||
|
|
|
||
|
|
LineGL2D shader{LineGL2D::Configuration{}
|
||
|
|
.setFlags(LineGL2D::Flag::VertexColor|flag)
|
||
|
|
.setCapStyle(LineCapStyle::Triangle)};
|
||
|
|
shader.setViewportSize(Vector2{RenderSizeSmall});
|
||
|
|
|
||
|
|
/* Set background to blue as well so we don't have too much aliasing */
|
||
|
|
GL::Renderer::setClearColor(0x000080_rgbf);
|
||
|
|
_framebuffer.clear(GL::FramebufferClear::Color);
|
||
|
|
|
||
|
|
if(flag == LineGL2D::Flag{}) {
|
||
|
|
shader
|
||
|
|
/* Background should stay blue, foreground should have no blue */
|
||
|
|
.setBackgroundColor(0x000080_rgbf)
|
||
|
|
.setColor(0x999900_rgbf)
|
||
|
|
.setWidth(4.0f)
|
||
|
|
.setSmoothness(1.0f)
|
||
|
|
.draw(lines);
|
||
|
|
} else if(flag == LineGL2D::Flag::UniformBuffers) {
|
||
|
|
GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
TransformationProjectionUniform2D{}
|
||
|
|
}};
|
||
|
|
GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineDrawUniform{}
|
||
|
|
}};
|
||
|
|
GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineMaterialUniform{}
|
||
|
|
/* Background should stay blue, foreground should have no blue */
|
||
|
|
.setBackgroundColor(0x000080_rgbf)
|
||
|
|
.setColor(0x999900_rgbf)
|
||
|
|
.setWidth(4.0f)
|
||
|
|
.setSmoothness(1.0f)
|
||
|
|
}};
|
||
|
|
shader
|
||
|
|
.bindTransformationProjectionBuffer(transformationProjectionUniform)
|
||
|
|
.bindDrawBuffer(drawUniform)
|
||
|
|
.bindMaterialBuffer(materialUniform)
|
||
|
|
.draw(lines);
|
||
|
|
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE();
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
|
||
|
|
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
|
||
|
|
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
|
||
|
|
|
||
|
|
CORRADE_COMPARE_WITH(
|
||
|
|
/* Dropping the alpha channel, as it's always 1.0 */
|
||
|
|
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
|
||
|
|
Utility::Path::join(SHADERS_TEST_DIR, "LineTestFiles/vertex-color.tga"),
|
||
|
|
(DebugTools::CompareImageToFile{_manager}));
|
||
|
|
}
|
||
|
|
|
||
|
|
template<class T, LineGL3D::Flag flag> void LineGLTest::renderVertexColor3D() {
|
||
|
|
if(flag == LineGL3D::Flag::UniformBuffers) {
|
||
|
|
setTestCaseTemplateName({T::Size == 3 ? "Color3" : "Color4", "Flag::UniformBuffers"});
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
} else {
|
||
|
|
setTestCaseTemplateName(T::Size == 3 ? "Color3" : "Color4");
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* Same as renderVertexColor2D(), except that the positions are 3D with
|
||
|
|
varying Z. But the (default) projection is orthographic so the output is
|
||
|
|
the same -- nothing 3D-specific to test here. */
|
||
|
|
GL::Mesh lines = generateLineMesh({
|
||
|
|
{-0.8f, 0.5f, 1.0f}, {-0.5f, -0.5f, -1.0f},
|
||
|
|
{-0.5f, -0.5f, -1.0f}, {0.0f, 0.0f, 0.0f},
|
||
|
|
{0.0f, 0.0f, 0.0f}, {0.5f, -0.5f, 0.5f},
|
||
|
|
{0.5f, -0.5f, 0.5f}, {0.8f, 0.5f, -1.0f}
|
||
|
|
});
|
||
|
|
|
||
|
|
/* Each line segment from above is four points */
|
||
|
|
T colors[]{
|
||
|
|
0xff0000_rgbf, 0xff0000_rgbf, 0xffff00_rgbf, 0xffff00_rgbf,
|
||
|
|
0xffff00_rgbf, 0xffff00_rgbf, 0x00ffff_rgbf, 0x00ffff_rgbf,
|
||
|
|
0x00ffff_rgbf, 0x00ffff_rgbf, 0x00ff00_rgbf, 0x00ff00_rgbf,
|
||
|
|
0x00ff00_rgbf, 0x00ff00_rgbf, 0x0000ff_rgbf, 0x0000ff_rgbf
|
||
|
|
};
|
||
|
|
if(std::is_same<T, Color3>::value)
|
||
|
|
lines.addVertexBuffer(GL::Buffer{GL::Buffer::TargetHint::Array, colors}, 0, LineGL3D::Color3{});
|
||
|
|
else
|
||
|
|
lines.addVertexBuffer(GL::Buffer{GL::Buffer::TargetHint::Array, colors}, 0, LineGL3D::Color4{});
|
||
|
|
|
||
|
|
LineGL3D shader{LineGL3D::Configuration{}
|
||
|
|
.setFlags(LineGL3D::Flag::VertexColor|flag)
|
||
|
|
.setCapStyle(LineCapStyle::Triangle)};
|
||
|
|
shader.setViewportSize(Vector2{RenderSizeSmall});
|
||
|
|
|
||
|
|
/* Set background to blue as well so we don't have too much aliasing */
|
||
|
|
GL::Renderer::setClearColor(0x000080_rgbf);
|
||
|
|
_framebuffer.clear(GL::FramebufferClear::Color);
|
||
|
|
|
||
|
|
if(flag == LineGL3D::Flag{}) {
|
||
|
|
shader
|
||
|
|
/* Background should stay blue, foreground should have no blue */
|
||
|
|
.setBackgroundColor(0x000080_rgbf)
|
||
|
|
.setColor(0x999900_rgbf)
|
||
|
|
.setWidth(4.0f)
|
||
|
|
.setSmoothness(1.0f)
|
||
|
|
.draw(lines);
|
||
|
|
} else if(flag == LineGL3D::Flag::UniformBuffers) {
|
||
|
|
GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
TransformationProjectionUniform3D{}
|
||
|
|
}};
|
||
|
|
GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineDrawUniform{}
|
||
|
|
}};
|
||
|
|
GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineMaterialUniform{}
|
||
|
|
/* Background should stay blue, foreground should have no blue */
|
||
|
|
.setBackgroundColor(0x000080_rgbf)
|
||
|
|
.setColor(0x999900_rgbf)
|
||
|
|
.setWidth(4.0f)
|
||
|
|
.setSmoothness(1.0f)
|
||
|
|
}};
|
||
|
|
shader
|
||
|
|
.bindTransformationProjectionBuffer(transformationProjectionUniform)
|
||
|
|
.bindDrawBuffer(drawUniform)
|
||
|
|
.bindMaterialBuffer(materialUniform)
|
||
|
|
.draw(lines);
|
||
|
|
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE();
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
|
||
|
|
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
|
||
|
|
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
|
||
|
|
|
||
|
|
CORRADE_COMPARE_WITH(
|
||
|
|
/* Dropping the alpha channel, as it's always 1.0 */
|
||
|
|
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
|
||
|
|
Utility::Path::join(SHADERS_TEST_DIR, "LineTestFiles/vertex-color.tga"),
|
||
|
|
(DebugTools::CompareImageToFile{_manager}));
|
||
|
|
}
|
||
|
|
|
||
|
|
template<LineGL2D::Flag flag> void LineGLTest::renderObjectId2D() {
|
||
|
|
auto&& data = RenderObjectIdData[testCaseInstanceId()];
|
||
|
|
setTestCaseDescription(data.name);
|
||
|
|
|
||
|
|
if(flag == LineGL3D::Flag::UniformBuffers) {
|
||
|
|
setTestCaseTemplateName("Flag::UniformBuffers");
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
GL::Mesh lines = generateLineMesh({
|
||
|
|
{-0.6f, 0.0f}, {0.6f, 0.0f}
|
||
|
|
});
|
||
|
|
|
||
|
|
LineGL2D shader{LineGL2D::Configuration{}
|
||
|
|
.setFlags(LineGL2D::Flag::ObjectId|flag)
|
||
|
|
.setCapStyle(data.capStyle)};
|
||
|
|
shader.setViewportSize(Vector2{RenderSizeSmall});
|
||
|
|
|
||
|
|
/* Map ObjectIdOutput so we can draw to it. Mapping it always causes an
|
||
|
|
error on WebGL when the shader does not render to it; however if not
|
||
|
|
bound we can't even clear it on WebGL, so it has to be cleared after. */
|
||
|
|
_framebuffer
|
||
|
|
.mapForDraw({
|
||
|
|
{LineGL2D::ColorOutput, GL::Framebuffer::ColorAttachment{0}},
|
||
|
|
{LineGL2D::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}}
|
||
|
|
})
|
||
|
|
.clearColor(1, Vector4ui{27});
|
||
|
|
|
||
|
|
if(flag == LineGL2D::Flag{}) {
|
||
|
|
shader
|
||
|
|
.setWidth(data.width)
|
||
|
|
.setSmoothness(data.smoothness)
|
||
|
|
.setObjectId(47365)
|
||
|
|
.draw(lines);
|
||
|
|
} else if(flag == LineGL2D::Flag::UniformBuffers) {
|
||
|
|
GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
TransformationProjectionUniform2D{}
|
||
|
|
}};
|
||
|
|
GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineDrawUniform{}
|
||
|
|
.setObjectId(47365)
|
||
|
|
}};
|
||
|
|
GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineMaterialUniform{}
|
||
|
|
.setWidth(data.width)
|
||
|
|
.setSmoothness(data.smoothness)
|
||
|
|
}};
|
||
|
|
shader
|
||
|
|
.bindTransformationProjectionBuffer(transformationProjectionUniform)
|
||
|
|
.bindDrawBuffer(drawUniform)
|
||
|
|
.bindMaterialBuffer(materialUniform)
|
||
|
|
.draw(lines);
|
||
|
|
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE();
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
/* No need to verify the whole image, just check that pixels at known
|
||
|
|
places have expected values. SwiftShader insists that the read format
|
||
|
|
has to be 32bit, so the renderbuffer format is that too to make it the
|
||
|
|
same (ES3 Mesa complains if these don't match). */
|
||
|
|
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1});
|
||
|
|
CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete);
|
||
|
|
Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI});
|
||
|
|
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{0});
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
/* Center of the line, should be set */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[39][39], 47365);
|
||
|
|
/* Corner of the line, should be set as well, independently of the cap
|
||
|
|
style or smoothness */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[35][11], 47365);
|
||
|
|
/* Outside of the object */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[34][11], 27);
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[35][10], 27);
|
||
|
|
}
|
||
|
|
|
||
|
|
template<LineGL3D::Flag flag> void LineGLTest::renderObjectId3D() {
|
||
|
|
auto&& data = RenderObjectIdData[testCaseInstanceId()];
|
||
|
|
setTestCaseDescription(data.name);
|
||
|
|
|
||
|
|
if(flag == LineGL3D::Flag::UniformBuffers) {
|
||
|
|
setTestCaseTemplateName("Flag::UniformBuffers");
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* Same as renderObjectId2D(), just with a varying Z coordinate (which
|
||
|
|
should have no effect as the projection is orthographic) */
|
||
|
|
GL::Mesh lines = generateLineMesh({
|
||
|
|
{-0.6f, 0.0f, 1.0f}, {0.6f, 0.0f, -1.0f}
|
||
|
|
});
|
||
|
|
|
||
|
|
LineGL3D shader{LineGL3D::Configuration{}
|
||
|
|
.setFlags(LineGL3D::Flag::ObjectId|flag)
|
||
|
|
.setCapStyle(data.capStyle)};
|
||
|
|
shader.setViewportSize(Vector2{RenderSizeSmall});
|
||
|
|
|
||
|
|
/* Map ObjectIdOutput so we can draw to it. Mapping it always causes an
|
||
|
|
error on WebGL when the shader does not render to it; however if not
|
||
|
|
bound we can't even clear it on WebGL, so it has to be cleared after. */
|
||
|
|
_framebuffer
|
||
|
|
.mapForDraw({
|
||
|
|
{LineGL3D::ColorOutput, GL::Framebuffer::ColorAttachment{0}},
|
||
|
|
{LineGL3D::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}}
|
||
|
|
})
|
||
|
|
.clearColor(1, Vector4ui{27});
|
||
|
|
|
||
|
|
if(flag == LineGL3D::Flag{}) {
|
||
|
|
shader
|
||
|
|
.setWidth(data.width)
|
||
|
|
.setSmoothness(data.smoothness)
|
||
|
|
.setObjectId(47365)
|
||
|
|
.draw(lines);
|
||
|
|
} else if(flag == LineGL3D::Flag::UniformBuffers) {
|
||
|
|
GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
TransformationProjectionUniform3D{}
|
||
|
|
}};
|
||
|
|
GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineDrawUniform{}
|
||
|
|
.setObjectId(47365)
|
||
|
|
}};
|
||
|
|
GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineMaterialUniform{}
|
||
|
|
.setWidth(data.width)
|
||
|
|
.setSmoothness(data.smoothness)
|
||
|
|
}};
|
||
|
|
shader
|
||
|
|
.bindTransformationProjectionBuffer(transformationProjectionUniform)
|
||
|
|
.bindDrawBuffer(drawUniform)
|
||
|
|
.bindMaterialBuffer(materialUniform)
|
||
|
|
.draw(lines);
|
||
|
|
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE();
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
/* No need to verify the whole image, just check that pixels at known
|
||
|
|
places have expected values. SwiftShader insists that the read format
|
||
|
|
has to be 32bit, so the renderbuffer format is that too to make it the
|
||
|
|
same (ES3 Mesa complains if these don't match). */
|
||
|
|
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1});
|
||
|
|
CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete);
|
||
|
|
Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI});
|
||
|
|
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{0});
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
/* Center of the line, should be set */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[39][39], 47365);
|
||
|
|
/* Corner of the line, should be set as well, independently of the cap
|
||
|
|
style or smoothness */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[35][11], 47365);
|
||
|
|
/* Outside of the object */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[34][11], 27);
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[35][10], 27);
|
||
|
|
}
|
||
|
|
|
||
|
|
template<LineGL2D::Flag flag> void LineGLTest::renderInstanced2D() {
|
||
|
|
auto&& data = RenderInstancedData[testCaseInstanceId()];
|
||
|
|
setTestCaseDescription(data.name);
|
||
|
|
|
||
|
|
if(flag == LineGL3D::Flag::UniformBuffers) {
|
||
|
|
setTestCaseTemplateName("Flag::UniformBuffers");
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* A wave, important property is that it passes through origin to make
|
||
|
|
object ID verification easier */
|
||
|
|
GL::Mesh lines = generateLineMesh({
|
||
|
|
{-0.8f, -0.8f}, {-0.5f, 0.5f},
|
||
|
|
{-0.5f, 0.5f}, {0.5f, -0.5f},
|
||
|
|
{0.5f, -0.5f}, {0.8f, 0.8f}
|
||
|
|
});
|
||
|
|
|
||
|
|
/* Three circles, each in a different location */
|
||
|
|
struct {
|
||
|
|
Matrix3 transformation;
|
||
|
|
Color3 color;
|
||
|
|
UnsignedInt objectId;
|
||
|
|
} instanceData[] {
|
||
|
|
{Matrix3::translation({-1.25f, -1.25f}), 0xffff00_rgbf, 211},
|
||
|
|
{Matrix3::translation({ 1.25f, -1.25f}), 0x00ffff_rgbf, 4627},
|
||
|
|
{Matrix3::translation({ 0.00f, 1.25f}), 0xff00ff_rgbf, 35363},
|
||
|
|
};
|
||
|
|
|
||
|
|
lines
|
||
|
|
.addVertexBufferInstanced(GL::Buffer{GL::Buffer::TargetHint::Array, instanceData}, 1, 0,
|
||
|
|
LineGL2D::TransformationMatrix{},
|
||
|
|
LineGL2D::Color3{},
|
||
|
|
LineGL2D::ObjectId{})
|
||
|
|
.setInstanceCount(3);
|
||
|
|
|
||
|
|
LineGL2D shader{LineGL2D::Configuration{}
|
||
|
|
.setFlags(LineGL2D::Flag::InstancedTransformation|LineGL2D::Flag::VertexColor|data.flags|flag)};
|
||
|
|
shader.setViewportSize(Vector2{RenderSizeSmall});
|
||
|
|
|
||
|
|
/* Map ObjectIdOutput so we can draw to it. Mapping it always causes an
|
||
|
|
error on WebGL when the shader does not render to it; however if not
|
||
|
|
bound we can't even clear it on WebGL, so it has to be cleared after. */
|
||
|
|
if(data.flags & LineGL2D::Flag::ObjectId) _framebuffer
|
||
|
|
.mapForDraw({
|
||
|
|
{LineGL2D::ColorOutput, GL::Framebuffer::ColorAttachment{0}},
|
||
|
|
{LineGL2D::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}}
|
||
|
|
})
|
||
|
|
.clearColor(1, Vector4ui{27});
|
||
|
|
|
||
|
|
if(flag == LineGL2D::Flag{}) {
|
||
|
|
shader
|
||
|
|
.setTransformationProjectionMatrix(
|
||
|
|
Matrix3::projection({2.1f, 2.1f})*
|
||
|
|
Matrix3::scaling(Vector2{0.4f}))
|
||
|
|
.setColor(0xffff00_rgbf)
|
||
|
|
.setWidth(5.0f)
|
||
|
|
.setSmoothness(1.0f);
|
||
|
|
if(data.flags & LineGL2D::Flag::ObjectId)
|
||
|
|
/* Gets added to the per-instance ID, if that's enabled as well */
|
||
|
|
shader.setObjectId(1000);
|
||
|
|
shader.draw(lines);
|
||
|
|
|
||
|
|
} else if(flag == LineGL2D::Flag::UniformBuffers) {
|
||
|
|
GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
TransformationProjectionUniform2D{}
|
||
|
|
.setTransformationProjectionMatrix(
|
||
|
|
Matrix3::projection({2.1f, 2.1f})*
|
||
|
|
Matrix3::scaling(Vector2{0.4f})
|
||
|
|
)
|
||
|
|
}};
|
||
|
|
GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineDrawUniform{}
|
||
|
|
/* Gets added to the per-instance ID, if that's enabled as
|
||
|
|
well */
|
||
|
|
.setObjectId(1000)
|
||
|
|
}};
|
||
|
|
GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineMaterialUniform{}
|
||
|
|
.setColor(0xffff00_rgbf)
|
||
|
|
.setWidth(5.0f)
|
||
|
|
.setSmoothness(1.0f)
|
||
|
|
}};
|
||
|
|
shader
|
||
|
|
.bindTransformationProjectionBuffer(transformationProjectionUniform)
|
||
|
|
.bindDrawBuffer(drawUniform)
|
||
|
|
.bindMaterialBuffer(materialUniform)
|
||
|
|
.draw(lines);
|
||
|
|
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE();
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
|
||
|
|
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
|
||
|
|
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
|
||
|
|
|
||
|
|
/* - First should be lower left, yellow with a yellow base color, so yellow
|
||
|
|
- Second lower right, cyan with a yellow base color, so green
|
||
|
|
- Third up center, magenta with a yellow base color, so red */
|
||
|
|
CORRADE_COMPARE_WITH(
|
||
|
|
/* Dropping the alpha channel, as it's always 1.0 */
|
||
|
|
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
|
||
|
|
Utility::Path::join(SHADERS_TEST_DIR, "LineTestFiles/instanced.tga"),
|
||
|
|
(DebugTools::CompareImageToFile{_manager}));
|
||
|
|
|
||
|
|
/* Object ID -- no need to verify the whole image, just check that pixels
|
||
|
|
on known places have expected values. SwiftShader insists that the read
|
||
|
|
format has to be 32bit, so the renderbuffer format is that too to make
|
||
|
|
it the same (ES3 Mesa complains if these don't match). */
|
||
|
|
if(data.flags & LineGL2D::Flag::ObjectId) {
|
||
|
|
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1});
|
||
|
|
CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete);
|
||
|
|
Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI});
|
||
|
|
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{0});
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
/* If instanced object IDs are enabled, the per-instance ID gets added
|
||
|
|
to the output as well */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[5][5], 27); /* Outside */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[20][20], data.expectedId[0]);
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[20][60], data.expectedId[1]);
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[60][40], data.expectedId[2]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
template<LineGL3D::Flag flag> void LineGLTest::renderInstanced3D() {
|
||
|
|
auto&& data = RenderInstancedData[testCaseInstanceId()];
|
||
|
|
setTestCaseDescription(data.name);
|
||
|
|
|
||
|
|
if(flag == LineGL3D::Flag::UniformBuffers) {
|
||
|
|
setTestCaseTemplateName("Flag::UniformBuffers");
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* Same as in renderInstanced2D() except for a varying Z coordinate. Which
|
||
|
|
shouldn't affect the output as the projection is orthographic. */
|
||
|
|
GL::Mesh lines = generateLineMesh({
|
||
|
|
{-0.8f, -0.8f, 1.0f}, {-0.5f, 0.5f, -1.0f},
|
||
|
|
{-0.5f, 0.5f, -1.0f}, {0.5f, -0.5f, 0.0f},
|
||
|
|
{0.5f, -0.5f, 0.0f}, {0.8f, 0.8f, 1.0f}
|
||
|
|
});
|
||
|
|
|
||
|
|
/* Three circles, each in a different location */
|
||
|
|
struct {
|
||
|
|
Matrix4 transformation;
|
||
|
|
Color3 color;
|
||
|
|
UnsignedInt objectId;
|
||
|
|
} instanceData[] {
|
||
|
|
{Matrix4::translation({-1.25f, -1.25f, 0.5f}), 0xffff00_rgbf, 211},
|
||
|
|
{Matrix4::translation({ 1.25f, -1.25f, -0.5f}), 0x00ffff_rgbf, 4627},
|
||
|
|
{Matrix4::translation({ 0.00f, 1.25f, 0.0f}), 0xff00ff_rgbf, 35363},
|
||
|
|
};
|
||
|
|
|
||
|
|
lines
|
||
|
|
.addVertexBufferInstanced(GL::Buffer{GL::Buffer::TargetHint::Array, instanceData}, 1, 0,
|
||
|
|
LineGL3D::TransformationMatrix{},
|
||
|
|
LineGL3D::Color3{},
|
||
|
|
LineGL3D::ObjectId{})
|
||
|
|
.setInstanceCount(3);
|
||
|
|
|
||
|
|
LineGL3D shader{LineGL3D::Configuration{}
|
||
|
|
.setFlags(LineGL3D::Flag::InstancedTransformation|LineGL3D::Flag::VertexColor|data.flags|flag)};
|
||
|
|
shader.setViewportSize(Vector2{RenderSizeSmall});
|
||
|
|
|
||
|
|
/* Map ObjectIdOutput so we can draw to it. Mapping it always causes an
|
||
|
|
error on WebGL when the shader does not render to it; however if not
|
||
|
|
bound we can't even clear it on WebGL, so it has to be cleared after. */
|
||
|
|
if(data.flags & LineGL3D::Flag::ObjectId) _framebuffer
|
||
|
|
.mapForDraw({
|
||
|
|
{LineGL3D::ColorOutput, GL::Framebuffer::ColorAttachment{0}},
|
||
|
|
{LineGL3D::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}}
|
||
|
|
})
|
||
|
|
.clearColor(1, Vector4ui{27});
|
||
|
|
|
||
|
|
if(flag == LineGL3D::Flag{}) {
|
||
|
|
shader
|
||
|
|
.setTransformationProjectionMatrix(
|
||
|
|
Matrix4::orthographicProjection({2.1f, 2.1f}, -1.0f, 1.0f)*
|
||
|
|
Matrix4::scaling(Vector3{0.4f}))
|
||
|
|
.setColor(0xffff00_rgbf)
|
||
|
|
.setWidth(5.0f)
|
||
|
|
.setSmoothness(1.0f);
|
||
|
|
if(data.flags & LineGL2D::Flag::ObjectId)
|
||
|
|
/* Gets added to the per-instance ID, if that's enabled as well */
|
||
|
|
shader.setObjectId(1000);
|
||
|
|
shader.draw(lines);
|
||
|
|
|
||
|
|
} else if(flag == LineGL2D::Flag::UniformBuffers) {
|
||
|
|
GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
TransformationProjectionUniform3D{}
|
||
|
|
.setTransformationProjectionMatrix(
|
||
|
|
Matrix4::orthographicProjection({2.1f, 2.1f}, -1.0f, 1.0f)*
|
||
|
|
Matrix4::scaling(Vector3{0.4f})
|
||
|
|
)
|
||
|
|
}};
|
||
|
|
GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineDrawUniform{}
|
||
|
|
/* Gets added to the per-instance ID, if that's enabled as
|
||
|
|
well */
|
||
|
|
.setObjectId(1000)
|
||
|
|
}};
|
||
|
|
GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, {
|
||
|
|
LineMaterialUniform{}
|
||
|
|
.setColor(0xffff00_rgbf)
|
||
|
|
.setWidth(5.0f)
|
||
|
|
.setSmoothness(1.0f)
|
||
|
|
}};
|
||
|
|
shader
|
||
|
|
.bindTransformationProjectionBuffer(transformationProjectionUniform)
|
||
|
|
.bindDrawBuffer(drawUniform)
|
||
|
|
.bindMaterialBuffer(materialUniform)
|
||
|
|
.draw(lines);
|
||
|
|
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE();
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
|
||
|
|
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
|
||
|
|
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
|
||
|
|
|
||
|
|
/* - First should be lower left, yellow with a yellow base color, so yellow
|
||
|
|
- Second lower right, cyan with a yellow base color, so green
|
||
|
|
- Third up center, magenta with a yellow base color, so red */
|
||
|
|
CORRADE_COMPARE_WITH(
|
||
|
|
/* Dropping the alpha channel, as it's always 1.0 */
|
||
|
|
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
|
||
|
|
Utility::Path::join(SHADERS_TEST_DIR, "LineTestFiles/instanced.tga"),
|
||
|
|
(DebugTools::CompareImageToFile{_manager}));
|
||
|
|
|
||
|
|
/* Object ID -- no need to verify the whole image, just check that pixels
|
||
|
|
on known places have expected values. SwiftShader insists that the read
|
||
|
|
format has to be 32bit, so the renderbuffer format is that too to make
|
||
|
|
it the same (ES3 Mesa complains if these don't match). */
|
||
|
|
if(data.flags & LineGL2D::Flag::ObjectId) {
|
||
|
|
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1});
|
||
|
|
CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete);
|
||
|
|
Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI});
|
||
|
|
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{0});
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
/* If instanced object IDs are enabled, the per-instance ID gets added
|
||
|
|
to the output as well */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[5][5], 27); /* Outside */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[20][20], data.expectedId[0]);
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[20][60], data.expectedId[1]);
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[60][40], data.expectedId[2]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void LineGLTest::renderMulti2D() {
|
||
|
|
auto&& data = RenderMultiData[testCaseInstanceId()];
|
||
|
|
setTestCaseDescription(data.name);
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if(data.flags >= LineGL2D::Flag::MultiDraw) {
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::shader_draw_parameters>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported.");
|
||
|
|
#elif !defined(MAGNUM_TARGET_WEBGL)
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ANGLE::multi_draw>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported.");
|
||
|
|
#else
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::WEBGL::multi_draw>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL)
|
||
|
|
if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader)
|
||
|
|
CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* All parts pass through origin to make object ID verification easier */
|
||
|
|
GL::Mesh lines = generateLineMesh({
|
||
|
|
/* A wave, same as in renderInstanced2D() */
|
||
|
|
{-0.8f, -0.8f}, {-0.5f, 0.5f}, // 0 to 5 + 6 to 12 for the join
|
||
|
|
{-0.5f, 0.5f}, {0.5f, -0.5f}, // 12 to 17 + 18 to 23 for the join
|
||
|
|
{0.5f, -0.5f}, {0.8f, 0.8f}, // 24 to 29
|
||
|
|
|
||
|
|
/* A cross */
|
||
|
|
{-0.8f, -0.8f}, {0.8f, 0.8f}, // 30 to 35
|
||
|
|
{0.8f, -0.8f}, {-0.8f, 0.8f}, // 36 to 41
|
||
|
|
|
||
|
|
/* A single point */
|
||
|
|
{0.0f, 0.0f}, {0.0f, 0.0f} // 42 to 47
|
||
|
|
});
|
||
|
|
CORRADE_COMPARE(lines.count(), 48);
|
||
|
|
|
||
|
|
GL::MeshView wave{lines};
|
||
|
|
wave.setCount(30);
|
||
|
|
GL::MeshView cross{lines};
|
||
|
|
cross.setCount(12)
|
||
|
|
.setIndexRange(30);
|
||
|
|
GL::MeshView point{lines};
|
||
|
|
point.setCount(6)
|
||
|
|
.setIndexRange(42);
|
||
|
|
|
||
|
|
LineGL2D shader{LineGL2D::Configuration{}
|
||
|
|
.setFlags(LineGL2D::Flag::UniformBuffers|data.flags)
|
||
|
|
.setMaterialCount(data.materialCount)
|
||
|
|
.setDrawCount(data.drawCount)};
|
||
|
|
shader.setViewportSize(Vector2{RenderSizeSmall});
|
||
|
|
|
||
|
|
/* Map ObjectIdOutput so we can draw to it. Mapping it always causes an
|
||
|
|
error on WebGL when the shader does not render to it; however if not
|
||
|
|
bound we can't even clear it on WebGL, so it has to be cleared after. */
|
||
|
|
if(data.flags & LineGL2D::Flag::ObjectId) _framebuffer
|
||
|
|
.mapForDraw({
|
||
|
|
{LineGL2D::ColorOutput, GL::Framebuffer::ColorAttachment{0}},
|
||
|
|
{LineGL2D::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}}
|
||
|
|
})
|
||
|
|
.clearColor(1, Vector4ui{27});
|
||
|
|
|
||
|
|
/* Some drivers have uniform offset alignment as high as 256, which means
|
||
|
|
the subsequent sets of uniforms have to be aligned to a multiply of it.
|
||
|
|
The data.uniformIncrement is set high enough to ensure that, in the
|
||
|
|
non-offset-bind case this value is 1. */
|
||
|
|
|
||
|
|
Containers::Array<LineMaterialUniform> materialData{data.uniformIncrement + 1};
|
||
|
|
materialData[0*data.uniformIncrement] = LineMaterialUniform{}
|
||
|
|
.setColor(0x0000ff_rgbf)
|
||
|
|
.setWidth(3.0f)
|
||
|
|
.setSmoothness(3.0f);
|
||
|
|
materialData[1*data.uniformIncrement] = LineMaterialUniform{}
|
||
|
|
.setColor(0xff0000_rgbf)
|
||
|
|
.setWidth(5.0f)
|
||
|
|
.setSmoothness(1.0f);
|
||
|
|
GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, materialData};
|
||
|
|
|
||
|
|
Containers::Array<TransformationProjectionUniform2D> transformationProjectionData{2*data.uniformIncrement + 1};
|
||
|
|
transformationProjectionData[0*data.uniformIncrement] = TransformationProjectionUniform2D{}
|
||
|
|
.setTransformationProjectionMatrix(
|
||
|
|
Matrix3::projection({2.1f, 2.1f})*
|
||
|
|
Matrix3::scaling(Vector2{0.4f})*
|
||
|
|
Matrix3::translation({-1.25f, -1.25f})
|
||
|
|
);
|
||
|
|
transformationProjectionData[1*data.uniformIncrement] = TransformationProjectionUniform2D{}
|
||
|
|
.setTransformationProjectionMatrix(
|
||
|
|
Matrix3::projection({2.1f, 2.1f})*
|
||
|
|
Matrix3::scaling(Vector2{0.4f})*
|
||
|
|
Matrix3::translation({ 1.25f, -1.25f})
|
||
|
|
);
|
||
|
|
transformationProjectionData[2*data.uniformIncrement] = TransformationProjectionUniform2D{}
|
||
|
|
.setTransformationProjectionMatrix(
|
||
|
|
Matrix3::projection({2.1f, 2.1f})*
|
||
|
|
Matrix3::scaling(Vector2{0.4f})*
|
||
|
|
Matrix3::translation({ 0.00f, 1.25f})
|
||
|
|
);
|
||
|
|
GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData};
|
||
|
|
|
||
|
|
Containers::Array<LineDrawUniform> drawData{2*data.uniformIncrement + 1};
|
||
|
|
/* Material offsets are zero if we have single draw, as those are
|
||
|
|
done with UBO offset bindings instead. */
|
||
|
|
drawData[0*data.uniformIncrement] = LineDrawUniform{}
|
||
|
|
.setMaterialId(data.drawCount == 1 ? 0 : 1)
|
||
|
|
.setObjectId(1211);
|
||
|
|
drawData[1*data.uniformIncrement] = LineDrawUniform{}
|
||
|
|
.setMaterialId(data.drawCount == 1 ? 0 : 0)
|
||
|
|
.setObjectId(5627);
|
||
|
|
drawData[2*data.uniformIncrement] = LineDrawUniform{}
|
||
|
|
.setMaterialId(data.drawCount == 1 ? 0 : 1)
|
||
|
|
.setObjectId(36363);
|
||
|
|
GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData};
|
||
|
|
|
||
|
|
/* Enabling blending so the overlap in X is rendered alright */
|
||
|
|
GL::Renderer::enable(GL::Renderer::Feature::Blending);
|
||
|
|
GL::Renderer::setBlendFunction(
|
||
|
|
GL::Renderer::BlendFunction::One,
|
||
|
|
GL::Renderer::BlendFunction::OneMinusSourceAlpha);
|
||
|
|
|
||
|
|
/* Just one draw, rebinding UBOs each time */
|
||
|
|
if(data.drawCount == 1) {
|
||
|
|
shader.bindMaterialBuffer(materialUniform,
|
||
|
|
1*data.uniformIncrement*sizeof(LineMaterialUniform),
|
||
|
|
sizeof(LineMaterialUniform));
|
||
|
|
shader.bindTransformationProjectionBuffer(transformationProjectionUniform,
|
||
|
|
0*data.uniformIncrement*sizeof(TransformationProjectionUniform2D),
|
||
|
|
sizeof(TransformationProjectionUniform2D));
|
||
|
|
shader.bindDrawBuffer(drawUniform,
|
||
|
|
0*data.uniformIncrement*sizeof(LineDrawUniform),
|
||
|
|
sizeof(LineDrawUniform));
|
||
|
|
shader.draw(wave);
|
||
|
|
|
||
|
|
shader.bindMaterialBuffer(materialUniform,
|
||
|
|
0*data.uniformIncrement*sizeof(LineMaterialUniform),
|
||
|
|
sizeof(LineMaterialUniform));
|
||
|
|
shader.bindTransformationProjectionBuffer(transformationProjectionUniform,
|
||
|
|
1*data.uniformIncrement*sizeof(TransformationProjectionUniform2D),
|
||
|
|
sizeof(TransformationProjectionUniform2D));
|
||
|
|
shader.bindDrawBuffer(drawUniform,
|
||
|
|
1*data.uniformIncrement*sizeof(LineDrawUniform),
|
||
|
|
sizeof(LineDrawUniform));
|
||
|
|
shader.draw(cross);
|
||
|
|
|
||
|
|
shader.bindMaterialBuffer(materialUniform,
|
||
|
|
1*data.uniformIncrement*sizeof(LineMaterialUniform),
|
||
|
|
sizeof(LineMaterialUniform));
|
||
|
|
shader.bindTransformationProjectionBuffer(transformationProjectionUniform,
|
||
|
|
2*data.uniformIncrement*sizeof(TransformationProjectionUniform2D),
|
||
|
|
sizeof(TransformationProjectionUniform2D));
|
||
|
|
shader.bindDrawBuffer(drawUniform,
|
||
|
|
2*data.uniformIncrement*sizeof(LineDrawUniform),
|
||
|
|
sizeof(LineDrawUniform));
|
||
|
|
shader.draw(point);
|
||
|
|
|
||
|
|
/* Otherwise using the draw offset / multidraw */
|
||
|
|
} else {
|
||
|
|
shader.bindTransformationProjectionBuffer(transformationProjectionUniform)
|
||
|
|
.bindDrawBuffer(drawUniform)
|
||
|
|
.bindMaterialBuffer(materialUniform);
|
||
|
|
|
||
|
|
if(data.flags >= LineGL2D::Flag::MultiDraw)
|
||
|
|
shader.draw({wave, cross, point});
|
||
|
|
else shader
|
||
|
|
.setDrawOffset(0)
|
||
|
|
.draw(wave)
|
||
|
|
.setDrawOffset(1)
|
||
|
|
.draw(cross)
|
||
|
|
.setDrawOffset(2)
|
||
|
|
.draw(point);
|
||
|
|
}
|
||
|
|
|
||
|
|
GL::Renderer::disable(GL::Renderer::Feature::Blending);
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
|
||
|
|
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
|
||
|
|
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
|
||
|
|
|
||
|
|
/* - Wave should be lower left, red
|
||
|
|
- Cross lower right, blue
|
||
|
|
- Point up center, red */
|
||
|
|
CORRADE_COMPARE_WITH(
|
||
|
|
/* Dropping the alpha channel, as it's always 1.0 */
|
||
|
|
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
|
||
|
|
Utility::Path::join(SHADERS_TEST_DIR, "LineTestFiles/multidraw.tga"),
|
||
|
|
(DebugTools::CompareImageToFile{_manager}));
|
||
|
|
|
||
|
|
/* Object ID -- no need to verify the whole image, just check that pixels
|
||
|
|
on known places have expected values. SwiftShader insists that the read
|
||
|
|
format has to be 32bit, so the renderbuffer format is that too to make
|
||
|
|
it the same (ES3 Mesa complains if these don't match). */
|
||
|
|
if(data.flags & LineGL2D::Flag::ObjectId) {
|
||
|
|
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1});
|
||
|
|
CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete);
|
||
|
|
Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI});
|
||
|
|
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{0});
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[5][5], 27); /* Outside */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[20][20], data.expectedId[0]); /* Wave */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[20][60], data.expectedId[1]); /* Cross */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[60][40], data.expectedId[2]); /* Point */
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void LineGLTest::renderMulti3D() {
|
||
|
|
auto&& data = RenderMultiData[testCaseInstanceId()];
|
||
|
|
setTestCaseDescription(data.name);
|
||
|
|
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::gpu_shader4>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::EXT::gpu_shader4::string() << "is not supported.");
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::uniform_buffer_object>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::uniform_buffer_object::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
if(data.flags >= LineGL2D::Flag::MultiDraw) {
|
||
|
|
#ifndef MAGNUM_TARGET_GLES
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::shader_draw_parameters>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ARB::shader_draw_parameters::string() << "is not supported.");
|
||
|
|
#elif !defined(MAGNUM_TARGET_WEBGL)
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ANGLE::multi_draw>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::ANGLE::multi_draw::string() << "is not supported.");
|
||
|
|
#else
|
||
|
|
if(!GL::Context::current().isExtensionSupported<GL::Extensions::WEBGL::multi_draw>())
|
||
|
|
CORRADE_SKIP(GL::Extensions::WEBGL::multi_draw::string() << "is not supported.");
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL)
|
||
|
|
if(GL::Context::current().detectedDriver() & GL::Context::DetectedDriver::SwiftShader)
|
||
|
|
CORRADE_SKIP("UBOs with dynamically indexed arrays are a crashy dumpster fire on SwiftShader, can't test.");
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* Same as in renderMulti2D() except for an additional varying Z
|
||
|
|
coordinate. Which won't affect the output because the projection is
|
||
|
|
orthographic. */
|
||
|
|
GL::Mesh lines = generateLineMesh({
|
||
|
|
{-0.8f, -0.8f, 1.0f}, {-0.5f, 0.5f, -1.0f},
|
||
|
|
{-0.5f, 0.5f, -1.0f}, {0.5f, -0.5f, 0.0f},
|
||
|
|
{0.5f, -0.5f, 0.0f}, {0.8f, 0.8f, 1.0f},
|
||
|
|
|
||
|
|
{-0.8f, -0.8f, 1.0f}, {0.8f, 0.8f, 1.0f},
|
||
|
|
{0.8f, -0.8f, -1.0f}, {-0.8f, 0.8f, -1.0f},
|
||
|
|
|
||
|
|
{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}
|
||
|
|
});
|
||
|
|
CORRADE_COMPARE(lines.count(), 48);
|
||
|
|
|
||
|
|
GL::MeshView wave{lines};
|
||
|
|
wave.setCount(30);
|
||
|
|
GL::MeshView cross{lines};
|
||
|
|
cross.setCount(12)
|
||
|
|
.setIndexRange(30);
|
||
|
|
GL::MeshView point{lines};
|
||
|
|
point.setCount(6)
|
||
|
|
.setIndexRange(42);
|
||
|
|
|
||
|
|
LineGL3D shader{LineGL3D::Configuration{}
|
||
|
|
.setFlags(LineGL3D::Flag::UniformBuffers|data.flags)
|
||
|
|
.setMaterialCount(data.materialCount)
|
||
|
|
.setDrawCount(data.drawCount)};
|
||
|
|
shader.setViewportSize(Vector2{RenderSizeSmall});
|
||
|
|
|
||
|
|
/* Map ObjectIdOutput so we can draw to it. Mapping it always causes an
|
||
|
|
error on WebGL when the shader does not render to it; however if not
|
||
|
|
bound we can't even clear it on WebGL, so it has to be cleared after. */
|
||
|
|
if(data.flags & LineGL3D::Flag::ObjectId) _framebuffer
|
||
|
|
.mapForDraw({
|
||
|
|
{LineGL3D::ColorOutput, GL::Framebuffer::ColorAttachment{0}},
|
||
|
|
{LineGL3D::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}}
|
||
|
|
})
|
||
|
|
.clearColor(1, Vector4ui{27});
|
||
|
|
|
||
|
|
/* Some drivers have uniform offset alignment as high as 256, which means
|
||
|
|
the subsequent sets of uniforms have to be aligned to a multiply of it.
|
||
|
|
The data.uniformIncrement is set high enough to ensure that, in the
|
||
|
|
non-offset-bind case this value is 1. */
|
||
|
|
|
||
|
|
Containers::Array<LineMaterialUniform> materialData{data.uniformIncrement + 1};
|
||
|
|
materialData[0*data.uniformIncrement] = LineMaterialUniform{}
|
||
|
|
.setColor(0x0000ff_rgbf)
|
||
|
|
.setWidth(3.0f)
|
||
|
|
.setSmoothness(3.0f);
|
||
|
|
materialData[1*data.uniformIncrement] = LineMaterialUniform{}
|
||
|
|
.setColor(0xff0000_rgbf)
|
||
|
|
.setWidth(5.0f)
|
||
|
|
.setSmoothness(1.0f);
|
||
|
|
GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, materialData};
|
||
|
|
|
||
|
|
Containers::Array<TransformationProjectionUniform3D> transformationProjectionData{2*data.uniformIncrement + 1};
|
||
|
|
transformationProjectionData[0*data.uniformIncrement] = TransformationProjectionUniform3D{}
|
||
|
|
.setTransformationProjectionMatrix(
|
||
|
|
Matrix4::orthographicProjection({2.1f, 2.1f}, -1.0f, 1.0f)*
|
||
|
|
Matrix4::scaling(Vector3{0.4f})*
|
||
|
|
Matrix4::translation({-1.25f, -1.25f, 0.5f})
|
||
|
|
);
|
||
|
|
transformationProjectionData[1*data.uniformIncrement] = TransformationProjectionUniform3D{}
|
||
|
|
.setTransformationProjectionMatrix(
|
||
|
|
Matrix4::orthographicProjection({2.1f, 2.1f}, -1.0f, 1.0f)*
|
||
|
|
Matrix4::scaling(Vector3{0.4f})*
|
||
|
|
Matrix4::translation({ 1.25f, -1.25f, -0.5f})
|
||
|
|
);
|
||
|
|
transformationProjectionData[2*data.uniformIncrement] = TransformationProjectionUniform3D{}
|
||
|
|
.setTransformationProjectionMatrix(
|
||
|
|
Matrix4::orthographicProjection({2.1f, 2.1f}, -1.0f, 1.0f)*
|
||
|
|
Matrix4::scaling(Vector3{0.4f})*
|
||
|
|
Matrix4::translation({ 0.00f, 1.25f, 0.0f})
|
||
|
|
);
|
||
|
|
GL::Buffer transformationProjectionUniform{GL::Buffer::TargetHint::Uniform, transformationProjectionData};
|
||
|
|
|
||
|
|
Containers::Array<LineDrawUniform> drawData{2*data.uniformIncrement + 1};
|
||
|
|
/* Material offsets are zero if we have single draw, as those are
|
||
|
|
done with UBO offset bindings instead. */
|
||
|
|
drawData[0*data.uniformIncrement] = LineDrawUniform{}
|
||
|
|
.setMaterialId(data.drawCount == 1 ? 0 : 1)
|
||
|
|
.setObjectId(1211);
|
||
|
|
drawData[1*data.uniformIncrement] = LineDrawUniform{}
|
||
|
|
.setMaterialId(data.drawCount == 1 ? 0 : 0)
|
||
|
|
.setObjectId(5627);
|
||
|
|
drawData[2*data.uniformIncrement] = LineDrawUniform{}
|
||
|
|
.setMaterialId(data.drawCount == 1 ? 0 : 1)
|
||
|
|
.setObjectId(36363);
|
||
|
|
GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, drawData};
|
||
|
|
|
||
|
|
/* Enabling blending so the overlap in X is rendered alright */
|
||
|
|
GL::Renderer::enable(GL::Renderer::Feature::Blending);
|
||
|
|
GL::Renderer::setBlendFunction(
|
||
|
|
GL::Renderer::BlendFunction::One,
|
||
|
|
GL::Renderer::BlendFunction::OneMinusSourceAlpha);
|
||
|
|
|
||
|
|
/* Just one draw, rebinding UBOs each time */
|
||
|
|
if(data.drawCount == 1) {
|
||
|
|
shader.bindMaterialBuffer(materialUniform,
|
||
|
|
1*data.uniformIncrement*sizeof(LineMaterialUniform),
|
||
|
|
sizeof(LineMaterialUniform));
|
||
|
|
shader.bindTransformationProjectionBuffer(transformationProjectionUniform,
|
||
|
|
0*data.uniformIncrement*sizeof(TransformationProjectionUniform3D),
|
||
|
|
sizeof(TransformationProjectionUniform3D));
|
||
|
|
shader.bindDrawBuffer(drawUniform,
|
||
|
|
0*data.uniformIncrement*sizeof(LineDrawUniform),
|
||
|
|
sizeof(LineDrawUniform));
|
||
|
|
shader.draw(wave);
|
||
|
|
|
||
|
|
shader.bindMaterialBuffer(materialUniform,
|
||
|
|
0*data.uniformIncrement*sizeof(LineMaterialUniform),
|
||
|
|
sizeof(LineMaterialUniform));
|
||
|
|
shader.bindTransformationProjectionBuffer(transformationProjectionUniform,
|
||
|
|
1*data.uniformIncrement*sizeof(TransformationProjectionUniform3D),
|
||
|
|
sizeof(TransformationProjectionUniform3D));
|
||
|
|
shader.bindDrawBuffer(drawUniform,
|
||
|
|
1*data.uniformIncrement*sizeof(LineDrawUniform),
|
||
|
|
sizeof(LineDrawUniform));
|
||
|
|
shader.draw(cross);
|
||
|
|
|
||
|
|
shader.bindMaterialBuffer(materialUniform,
|
||
|
|
1*data.uniformIncrement*sizeof(LineMaterialUniform),
|
||
|
|
sizeof(LineMaterialUniform));
|
||
|
|
shader.bindTransformationProjectionBuffer(transformationProjectionUniform,
|
||
|
|
2*data.uniformIncrement*sizeof(TransformationProjectionUniform3D),
|
||
|
|
sizeof(TransformationProjectionUniform3D));
|
||
|
|
shader.bindDrawBuffer(drawUniform,
|
||
|
|
2*data.uniformIncrement*sizeof(LineDrawUniform),
|
||
|
|
sizeof(LineDrawUniform));
|
||
|
|
shader.draw(point);
|
||
|
|
|
||
|
|
/* Otherwise using the draw offset / multidraw */
|
||
|
|
} else {
|
||
|
|
shader.bindTransformationProjectionBuffer(transformationProjectionUniform)
|
||
|
|
.bindDrawBuffer(drawUniform)
|
||
|
|
.bindMaterialBuffer(materialUniform);
|
||
|
|
|
||
|
|
if(data.flags >= LineGL3D::Flag::MultiDraw)
|
||
|
|
shader.draw({wave, cross, point});
|
||
|
|
else shader
|
||
|
|
.setDrawOffset(0)
|
||
|
|
.draw(wave)
|
||
|
|
.setDrawOffset(1)
|
||
|
|
.draw(cross)
|
||
|
|
.setDrawOffset(2)
|
||
|
|
.draw(point);
|
||
|
|
}
|
||
|
|
|
||
|
|
GL::Renderer::disable(GL::Renderer::Feature::Blending);
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
|
||
|
|
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
|
||
|
|
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
|
||
|
|
|
||
|
|
/* - Wave should be lower left, red
|
||
|
|
- Cross lower right, blue
|
||
|
|
- Point up center, red */
|
||
|
|
CORRADE_COMPARE_WITH(
|
||
|
|
/* Dropping the alpha channel, as it's always 1.0 */
|
||
|
|
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
|
||
|
|
Utility::Path::join(SHADERS_TEST_DIR, "LineTestFiles/multidraw.tga"),
|
||
|
|
(DebugTools::CompareImageToFile{_manager}));
|
||
|
|
|
||
|
|
/* Object ID -- no need to verify the whole image, just check that pixels
|
||
|
|
on known places have expected values. SwiftShader insists that the read
|
||
|
|
format has to be 32bit, so the renderbuffer format is that too to make
|
||
|
|
it the same (ES3 Mesa complains if these don't match). */
|
||
|
|
if(data.flags & LineGL3D::Flag::ObjectId) {
|
||
|
|
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1});
|
||
|
|
CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete);
|
||
|
|
Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI});
|
||
|
|
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{0});
|
||
|
|
|
||
|
|
MAGNUM_VERIFY_NO_GL_ERROR();
|
||
|
|
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[5][5], 27); /* Outside */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[20][20], data.expectedId[0]); /* Wave */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[20][60], data.expectedId[1]); /* Cross */
|
||
|
|
CORRADE_COMPARE(image.pixels<UnsignedInt>()[60][40], data.expectedId[2]); /* Point */
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
}}}}
|
||
|
|
|
||
|
|
CORRADE_TEST_MAIN(Magnum::Shaders::Test::LineGLTest)
|