Browse Source

Shaders: skinning support in FlatGL, MeshVisualizerGL and PhongGL.

High-level docs with examples will be written once there's corresponding
support in MeshTools::compile() *and* in importer plugins, as skinned
meshes are usually brought in from files, never set up directly.

Co-authored-by: Squareys <squareys@googlemail.com>
pull/499/head
Vladimír Vondruš 3 years ago
parent
commit
89a6eb22de
  1. 7
      src/Magnum/Shaders/Flat.frag
  2. 64
      src/Magnum/Shaders/Flat.h
  3. 149
      src/Magnum/Shaders/Flat.vert
  4. 174
      src/Magnum/Shaders/FlatGL.cpp
  5. 371
      src/Magnum/Shaders/FlatGL.h
  6. 7
      src/Magnum/Shaders/MeshVisualizer.frag
  7. 7
      src/Magnum/Shaders/MeshVisualizer.geom
  8. 128
      src/Magnum/Shaders/MeshVisualizer.h
  9. 141
      src/Magnum/Shaders/MeshVisualizer.vert
  10. 284
      src/Magnum/Shaders/MeshVisualizerGL.cpp
  11. 705
      src/Magnum/Shaders/MeshVisualizerGL.h
  12. 9
      src/Magnum/Shaders/Phong.frag
  13. 61
      src/Magnum/Shaders/Phong.h
  14. 104
      src/Magnum/Shaders/Phong.vert
  15. 171
      src/Magnum/Shaders/PhongGL.cpp
  16. 365
      src/Magnum/Shaders/PhongGL.h
  17. 20
      src/Magnum/Shaders/Test/CMakeLists.txt
  18. 1400
      src/Magnum/Shaders/Test/FlatGLTest.cpp
  19. 54
      src/Magnum/Shaders/Test/FlatGL_Test.cpp
  20. 26
      src/Magnum/Shaders/Test/FlatTest.cpp
  21. 1878
      src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp
  22. 67
      src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp
  23. 52
      src/Magnum/Shaders/Test/MeshVisualizerTest.cpp
  24. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/skinning-default.tga
  25. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/skinning-instanced.tga
  26. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/skinning-multi.tga
  27. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/skinning.tga
  28. 937
      src/Magnum/Shaders/Test/PhongGLTest.cpp
  29. 50
      src/Magnum/Shaders/Test/PhongGL_Test.cpp
  30. 24
      src/Magnum/Shaders/Test/PhongTest.cpp
  31. BIN
      src/Magnum/Shaders/Test/TestFiles/skinning-default.tga
  32. BIN
      src/Magnum/Shaders/Test/TestFiles/skinning-instanced.tga
  33. BIN
      src/Magnum/Shaders/Test/TestFiles/skinning-multi.tga
  34. BIN
      src/Magnum/Shaders/Test/TestFiles/skinning.tga

7
src/Magnum/Shaders/Flat.frag

@ -88,9 +88,10 @@ uniform highp uint drawOffset
#endif
struct DrawUniform {
highp uvec4 materialIdReservedObjectIdReservedReserved;
#define draw_materialIdReserved materialIdReservedObjectIdReservedReserved.x
#define draw_objectId materialIdReservedObjectIdReservedReserved.y
highp uvec4 materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved;
#define draw_materialIdReserved materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved.x
#define draw_objectId materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved.y
#define draw_jointOffsetPerInstanceJointCount materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved.z
};
layout(std140

64
src/Magnum/Shaders/Flat.h

@ -54,7 +54,13 @@ be shared among multiple draw calls and thus are provided in a separate
*/
struct FlatDrawUniform {
/** @brief Construct with default parameters */
constexpr explicit FlatDrawUniform(DefaultInitT = DefaultInit) noexcept: materialId{0}, objectId{0} {}
constexpr explicit FlatDrawUniform(DefaultInitT = DefaultInit) noexcept: materialId{0}, objectId{0},
#ifndef CORRADE_TARGET_BIG_ENDIAN
jointOffset{0}, perInstanceJointCount{0}
#else
perInstanceJointCount{0}, jointOffset{0}
#endif
{}
/** @brief Construct without initializing the contents */
explicit FlatDrawUniform(NoInitT) noexcept {}
@ -86,6 +92,24 @@ struct FlatDrawUniform {
return *this;
}
/**
* @brief Set the @ref jointOffset field
* @return Reference to self (for method chaining)
*/
FlatDrawUniform& setJointOffset(UnsignedInt offset) {
jointOffset = offset;
return *this;
}
/**
* @brief Set the @ref perInstanceJointCount field
* @return Reference to self (for method chaining)
*/
FlatDrawUniform& setPerInstanceJointCount(UnsignedInt count) {
perInstanceJointCount = count;
return *this;
}
/**
* @}
*/
@ -109,10 +133,10 @@ struct FlatDrawUniform {
/* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK
I MADE THOSE UNNAMED, YOU DUMB FOOL */
#ifndef DOXYGEN_GENERATING_OUTPUT
UnsignedShort:16; /* reserved for skinOffset */
UnsignedShort:16; /* reserved */
#endif
#else
UnsignedShort:16; /* reserved for skinOffset */
UnsignedShort:16; /* reserved */
UnsignedShort materialId;
#endif
@ -131,11 +155,43 @@ struct FlatDrawUniform {
*/
UnsignedInt objectId;
/** @var jointOffset
* @brief Joint offset
*
* Offset added to joint IDs in the @ref FlatGL::JointIds and
* @ref FlatGL::SecondaryJointIds attributes. Useful when a UBO with joint
* matrices for more than one skin is supplied or in a multi-draw scenario.
* Should be less than the joint count passed to
* @ref FlatGL::Configuration::setJointCount(). Default value is
* @cpp 0 @ce, meaning no offset is added to joint IDs.
*/
/** @var perInstanceJointCount
* @brief Per-instance joint count
*
* Offset added to joint IDs in the @ref FlatGL::JointIds and
* @ref FlatGL::SecondaryJointIds atttributes in instanced draws. Should
* be less than the joint count passed to
* @ref FlatGL::Configuration::setJointCount(). Default value is
* @cpp 0 @ce, meaning every instance will use the same joint matrices,
* setting it to a non-zero value causes the joint IDs to be interpreted as
* @glsl gl_InstanceID*count + jointId @ce.
*/
/* This field is an UnsignedInt in the shader and jointOffset is extracted
as (value & 0xffff), so the order has to be different on BE */
#ifndef CORRADE_TARGET_BIG_ENDIAN
UnsignedShort jointOffset;
UnsignedShort perInstanceJointCount;
#else
UnsignedShort perInstanceJointCount;
UnsignedShort jointOffset;
#endif
/* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK
I MADE THOSE UNNAMED, YOU DUMB FOOL */
#ifndef DOXYGEN_GENERATING_OUTPUT
Int:32;
Int:32;
#endif
};

149
src/Magnum/Shaders/Flat.vert

@ -48,6 +48,19 @@
#define const
#endif
/* Both classic uniforms and uniform buffers */
#ifdef DYNAMIC_PER_VERTEX_JOINT_COUNT
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = PER_VERTEX_JOINT_COUNT_LOCATION)
#endif
uniform mediump uvec2 perVertexJointCount
#ifndef GL_ES
= uvec2(PER_VERTEX_JOINT_COUNT, SECONDARY_PER_VERTEX_JOINT_COUNT)
#endif
;
#endif
/* Uniforms */
#ifndef UNIFORM_BUFFERS
@ -89,6 +102,32 @@ layout(location = 2)
uniform highp uint textureLayer; /* defaults to zero */
#endif
#ifdef JOINT_COUNT
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 6)
#endif
#ifdef TWO_DIMENSIONS
uniform mat3 jointMatrices[JOINT_COUNT]
#ifndef GL_ES
= mat3[](JOINT_MATRIX_INITIALIZER)
#endif
;
#elif defined(THREE_DIMENSIONS)
uniform mat4 jointMatrices[JOINT_COUNT]
#ifndef GL_ES
= mat4[](JOINT_MATRIX_INITIALIZER)
#endif
;
#else
#error
#endif
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = PER_INSTANCE_JOINT_COUNT_LOCATION)
#endif
uniform uint perInstanceJointCount; /* defaults to zero */
#endif
/* Uniform buffers */
#else
@ -105,6 +144,21 @@ uniform highp uint drawOffset
#define drawOffset 0u
#endif
struct DrawUniform {
highp uvec4 materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved;
#define draw_materialIdReserved materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved.x
#define draw_objectId materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved.y
#define draw_jointOffsetPerInstanceJointCount materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved.z
};
layout(std140
#ifdef EXPLICIT_BINDING
, binding = 2
#endif
) uniform Draw {
DrawUniform draws[DRAW_COUNT];
};
layout(std140
#ifdef EXPLICIT_BINDING
, binding = 1
@ -123,6 +177,26 @@ layout(std140
transformationProjectionMatrices[DRAW_COUNT];
};
#ifdef JOINT_COUNT
layout(std140
#ifdef EXPLICIT_BINDING
, binding = 6
#endif
) uniform Joint {
highp
#ifdef TWO_DIMENSIONS
/* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for
details */
mat3x4
#elif defined(THREE_DIMENSIONS)
mat4
#else
#error
#endif
jointMatrices[JOINT_COUNT];
};
#endif
#ifdef TEXTURE_TRANSFORMATION
struct TextureTransformationUniform {
highp vec4 rotationScaling;
@ -168,6 +242,32 @@ layout(location = COLOR_ATTRIBUTE_LOCATION)
in lowp vec4 vertexColor;
#endif
#ifdef JOINT_COUNT
#if PER_VERTEX_JOINT_COUNT
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = WEIGHTS_ATTRIBUTE_LOCATION)
#endif
in mediump vec4 weights;
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = JOINTIDS_ATTRIBUTE_LOCATION)
#endif
in mediump uvec4 jointIds;
#endif
#if SECONDARY_PER_VERTEX_JOINT_COUNT
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = SECONDARY_WEIGHTS_ATTRIBUTE_LOCATION)
#endif
in mediump vec4 secondaryWeights;
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = SECONDARY_JOINTIDS_ATTRIBUTE_LOCATION)
#endif
in mediump uvec4 secondaryJointIds;
#endif
#endif
#ifdef INSTANCED_OBJECT_ID
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = OBJECT_ID_ATTRIBUTE_LOCATION)
@ -252,6 +352,49 @@ void main() {
highp const uint textureLayer = floatBitsToUint(textureTransformations[drawId].textureTransformation_layer);
#endif
#endif
#ifdef JOINT_COUNT
mediump const uint jointOffset = (draws[drawId].draw_jointOffsetPerInstanceJointCount & 0xffffu) + uint(gl_InstanceID)*(draws[drawId].draw_jointOffsetPerInstanceJointCount >> 16 & 0xffffu);
#endif
#else
#ifdef JOINT_COUNT
mediump const uint jointOffset = uint(gl_InstanceID)*perInstanceJointCount;
#endif
#endif
#ifdef JOINT_COUNT
#ifdef TWO_DIMENSIONS
mat3 skinMatrix = mat3(0.0);
#elif defined(THREE_DIMENSIONS)
mat4 skinMatrix = mat4(0.0);
#else
#error
#endif
#if PER_VERTEX_JOINT_COUNT
for(uint i = 0u; i != PER_VERTEX_JOINT_COUNT
#ifdef DYNAMIC_PER_VERTEX_JOINT_COUNT
&& i != perVertexJointCount.x
#endif
; ++i)
skinMatrix += weights[i]*
#ifdef TWO_DIMENSIONS
mat3 /* need to slice because it's a mat3x4 due to ANGLE, see
DrawUniform in Phong.vert for details */
#endif
(jointMatrices[jointOffset + jointIds[i]]);
#endif
#if SECONDARY_PER_VERTEX_JOINT_COUNT
for(uint i = 0u; i != SECONDARY_PER_VERTEX_JOINT_COUNT
#ifdef DYNAMIC_PER_VERTEX_JOINT_COUNT
&& i != perVertexJointCount.y
#endif
; ++i)
skinMatrix += secondaryWeights[i]*
#ifdef TWO_DIMENSIONS
mat3 /* need to slice because it's a mat3x4 due to ANGLE, see
DrawUniform in Phong.vert for details */
#endif
(jointMatrices[jointOffset + secondaryJointIds[i]]);
#endif
#endif
#ifdef TWO_DIMENSIONS
@ -259,12 +402,18 @@ void main() {
#ifdef INSTANCED_TRANSFORMATION
instancedTransformationMatrix*
#endif
#ifdef JOINT_COUNT
skinMatrix*
#endif
vec3(position, 1.0), 0.0);
#elif defined(THREE_DIMENSIONS)
gl_Position = transformationProjectionMatrix*
#ifdef INSTANCED_TRANSFORMATION
instancedTransformationMatrix*
#endif
#ifdef JOINT_COUNT
skinMatrix*
#endif
position;
#else
#error

174
src/Magnum/Shaders/FlatGL.cpp

@ -49,6 +49,8 @@
namespace Magnum { namespace Shaders {
using namespace Containers::Literals;
namespace {
enum: Int {
TextureUnit = 0,
@ -58,13 +60,17 @@ namespace {
#ifndef MAGNUM_TARGET_GLES2
enum: Int {
/* Not using the zero binding to avoid conflicts with
ProjectionBufferBinding from other shaders which can likely stay
bound to the same buffer for the whole time */
/* Texture transformation and joints is slots 3 and 6 in all shaders so
haders can be switched without rebinding everything. Not using the
zero binding to avoid conflicts with ProjectionBufferBinding from
other shaders which can likely stay bound to the same buffer for the
whole time. */
TransformationProjectionBufferBinding = 1,
DrawBufferBinding = 2,
TextureTransformationBufferBinding = 3,
MaterialBufferBinding = 4
MaterialBufferBinding = 4,
/* 5 unused */
JointBufferBinding = 6,
};
#endif
}
@ -96,6 +102,13 @@ template<UnsignedInt dimensions> typename FlatGL<dimensions>::CompileState FlatG
"Shaders::FlatGL: texture arrays require texture transformation enabled as well if uniform buffers are used", CompileState{NoCreate});
#endif
#ifndef MAGNUM_TARGET_GLES2
CORRADE_ASSERT(!(configuration.flags() & Flag::DynamicPerVertexJointCount) || configuration.jointCount(),
"Shaders::FlatGL: dynamic per-vertex joint count enabled for zero joints", CompileState{NoCreate});
CORRADE_ASSERT(!(configuration.flags() & Flag::InstancedTransformation) || !configuration.secondaryPerVertexJointCount(),
"Shaders::FlatGL: TransformationMatrix attribute binding conflicts with the SecondaryJointIds / SecondaryWeights attributes, use a non-instanced rendering with secondary weights instead", CompileState{NoCreate});
#endif
#ifndef MAGNUM_TARGET_GLES
if(configuration.flags() >= Flag::UniformBuffers)
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object);
@ -131,6 +144,19 @@ template<UnsignedInt dimensions> typename FlatGL<dimensions>::CompileState FlatG
const GL::Version version = context.supportedVersion({GL::Version::GLES300, GL::Version::GLES200});
#endif
FlatGL<dimensions> out{NoInit};
out._flags = configuration.flags();
#ifndef MAGNUM_TARGET_GLES2
out._jointCount = configuration.jointCount();
out._perVertexJointCount = configuration.perVertexJointCount();
out._secondaryPerVertexJointCount = configuration.secondaryPerVertexJointCount();
out._materialCount = configuration.materialCount();
out._drawCount = configuration.drawCount();
out._perInstanceJointCountUniform = out._jointMatricesUniform + configuration.jointCount();
out._perVertexJointCountUniform = configuration.flags() >= Flag::UniformBuffers ?
1 : out._perInstanceJointCountUniform + 1;
#endif
GL::Shader vert = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Vertex);
GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment);
@ -151,6 +177,31 @@ template<UnsignedInt dimensions> typename FlatGL<dimensions>::CompileState FlatG
.addSource(configuration.flags() & Flag::InstancedTransformation ? "#define INSTANCED_TRANSFORMATION\n" : "")
.addSource(configuration.flags() >= Flag::InstancedTextureOffset ? "#define INSTANCED_TEXTURE_OFFSET\n" : "");
#ifndef MAGNUM_TARGET_GLES2
if(configuration.jointCount()) {
vert.addSource(Utility::formatString(
"#define JOINT_COUNT {}\n"
"#define PER_VERTEX_JOINT_COUNT {}u\n"
"#define SECONDARY_PER_VERTEX_JOINT_COUNT {}u\n"
#ifndef MAGNUM_TARGET_GLES
"#define JOINT_MATRIX_INITIALIZER {}\n"
#endif
"#define PER_INSTANCE_JOINT_COUNT_LOCATION {}\n",
configuration.jointCount(),
configuration.perVertexJointCount(),
configuration.secondaryPerVertexJointCount(),
#ifndef MAGNUM_TARGET_GLES
((dimensions == 2 ? "mat3(1.0), "_s : "mat4(1.0), "_s)*configuration.jointCount()).exceptSuffix(2),
#endif
out._perInstanceJointCountUniform));
}
if(configuration.flags() >= Flag::DynamicPerVertexJointCount) {
vert.addSource(Utility::formatString(
"#define DYNAMIC_PER_VERTEX_JOINT_COUNT\n"
"#define PER_VERTEX_JOINT_COUNT_LOCATION {}\n",
out._perVertexJointCountUniform));
}
#endif
#ifndef MAGNUM_TARGET_GLES2
if(configuration.flags() >= Flag::UniformBuffers) {
vert.addSource(Utility::formatString(
"#define UNIFORM_BUFFERS\n"
@ -190,13 +241,6 @@ template<UnsignedInt dimensions> typename FlatGL<dimensions>::CompileState FlatG
vert.submitCompile();
frag.submitCompile();
FlatGL<dimensions> out{NoInit};
out._flags = configuration.flags();
#ifndef MAGNUM_TARGET_GLES2
out._materialCount = configuration.materialCount();
out._drawCount = configuration.drawCount();
#endif
out.attachShaders({vert, frag});
/* ES3 has this done in the shader directly and doesn't even provide
@ -227,6 +271,19 @@ template<UnsignedInt dimensions> typename FlatGL<dimensions>::CompileState FlatG
out.bindAttributeLocation(TransformationMatrix::Location, "instancedTransformationMatrix");
if(configuration.flags() >= Flag::InstancedTextureOffset)
out.bindAttributeLocation(TextureOffset::Location, "instancedTextureOffset");
#ifndef MAGNUM_TARGET_GLES2
/* Configuration::setJointCount() checks that jointCount and
perVertexJointCount / secondaryPerVertexJointCount are either all
zero or non-zero so we don't need to check for jointCount() here */
if(configuration.perVertexJointCount()) {
out.bindAttributeLocation(Weights::Location, "weights");
out.bindAttributeLocation(JointIds::Location, "jointIds");
}
if(configuration.secondaryPerVertexJointCount()) {
out.bindAttributeLocation(SecondaryWeights::Location, "secondaryWeights");
out.bindAttributeLocation(SecondaryJointIds::Location, "secondaryJointIds");
}
#endif
}
#endif
@ -270,6 +327,8 @@ template<UnsignedInt dimensions> FlatGL<dimensions>::FlatGL(CompileState&& state
#endif
{
#ifndef MAGNUM_TARGET_GLES2
if(_flags >= Flag::DynamicPerVertexJointCount)
_perVertexJointCountUniform = uniformLocation("perVertexJointCount");
if(_flags >= Flag::UniformBuffers) {
if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset");
} else
@ -287,6 +346,12 @@ template<UnsignedInt dimensions> FlatGL<dimensions>::FlatGL(CompileState&& state
#ifndef MAGNUM_TARGET_GLES2
if(_flags & Flag::ObjectId) _objectIdUniform = uniformLocation("objectId");
#endif
#ifndef MAGNUM_TARGET_GLES2
if(_jointCount) {
_jointMatricesUniform = uniformLocation("jointMatrices");
_perInstanceJointCountUniform = uniformLocation("perInstanceJointCount");
}
#endif
}
}
@ -303,6 +368,8 @@ template<UnsignedInt dimensions> FlatGL<dimensions>::FlatGL(CompileState&& state
if(_flags & Flag::TextureTransformation)
setUniformBlockBinding(uniformBlockIndex("TextureTransformation"), TextureTransformationBufferBinding);
setUniformBlockBinding(uniformBlockIndex("Material"), MaterialBufferBinding);
if(_jointCount)
setUniformBlockBinding(uniformBlockIndex("Joint"), JointBufferBinding);
}
#endif
}
@ -310,6 +377,10 @@ template<UnsignedInt dimensions> FlatGL<dimensions>::FlatGL(CompileState&& state
/* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */
#ifdef MAGNUM_TARGET_GLES
#ifndef MAGNUM_TARGET_GLES2
if(_flags >= Flag::DynamicPerVertexJointCount)
setPerVertexJointCount(_perVertexJointCount, _secondaryPerVertexJointCount);
#endif
#ifndef MAGNUM_TARGET_GLES2
if(_flags >= Flag::UniformBuffers) {
/* Draw offset is zero by default */
} else
@ -322,6 +393,12 @@ template<UnsignedInt dimensions> FlatGL<dimensions>::FlatGL(CompileState&& state
setColor(Magnum::Color4{1.0f});
if(_flags & Flag::AlphaMask) setAlphaMask(0.5f);
/* Object ID is zero by default */
#ifndef MAGNUM_TARGET_GLES2
if(_jointCount) {
setJointMatrices(Containers::Array<MatrixTypeFor<dimensions, Float>>{DirectInit, _jointCount, Math::IdentityInit});
/* Per-instance joint count is zero by default */
}
#endif
}
#endif
}
@ -344,6 +421,19 @@ template<UnsignedInt dimensions> FlatGL<dimensions>::FlatGL(const Flags flags, c
template<UnsignedInt dimensions> FlatGL<dimensions>::FlatGL(NoInitT) {}
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> FlatGL<dimensions>& FlatGL<dimensions>::setPerVertexJointCount(const UnsignedInt count, const UnsignedInt secondaryCount) {
CORRADE_ASSERT(_flags >= Flag::DynamicPerVertexJointCount,
"Shaders::FlatGL::setPerVertexJointCount(): the shader was not created with dynamic per-vertex joint count enabled", *this);
CORRADE_ASSERT(count <= _perVertexJointCount,
"Shaders::FlatGL::setPerVertexJointCount(): expected at most" << _perVertexJointCount << "per-vertex joints, got" << count, *this);
CORRADE_ASSERT(secondaryCount <= _secondaryPerVertexJointCount,
"Shaders::FlatGL::setPerVertexJointCount(): expected at most" << _secondaryPerVertexJointCount << "secondary per-vertex joints, got" << secondaryCount, *this);
setUniform(_perVertexJointCountUniform, Vector2ui{count, secondaryCount});
return *this;
}
#endif
template<UnsignedInt dimensions> FlatGL<dimensions>& FlatGL<dimensions>::setTransformationProjectionMatrix(const MatrixTypeFor<dimensions, Float>& matrix) {
#ifndef MAGNUM_TARGET_GLES2
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
@ -404,6 +494,35 @@ template<UnsignedInt dimensions> FlatGL<dimensions>& FlatGL<dimensions>::setObje
setUniform(_objectIdUniform, id);
return *this;
}
template<UnsignedInt dimensions> FlatGL<dimensions>& FlatGL<dimensions>::setJointMatrices(const Containers::ArrayView<const MatrixTypeFor<dimensions, Float>> matrices) {
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
"Shaders::FlatGL::setJointMatrices(): the shader was created with uniform buffers enabled", *this);
CORRADE_ASSERT(_jointCount == matrices.size(),
"Shaders::FlatGL::setJointMatrices(): expected" << _jointCount << "items but got" << matrices.size(), *this);
if(_jointCount) setUniform(_jointMatricesUniform, matrices);
return *this;
}
template<UnsignedInt dimensions> FlatGL<dimensions>& FlatGL<dimensions>::setJointMatrices(const std::initializer_list<MatrixTypeFor<dimensions, Float>> matrices) {
return setJointMatrices(Containers::arrayView(matrices));
}
template<UnsignedInt dimensions> FlatGL<dimensions>& FlatGL<dimensions>::setJointMatrix(const UnsignedInt id, const MatrixTypeFor<dimensions, Float>& matrix) {
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
"Shaders::FlatGL::setJointMatrix(): the shader was created with uniform buffers enabled", *this);
CORRADE_ASSERT(id < _jointCount,
"Shaders::FlatGL::setJointMatrix(): joint ID" << id << "is out of bounds for" << _jointCount << "joints", *this);
setUniform(_jointMatricesUniform + id, matrix);
return *this;
}
template<UnsignedInt dimensions> FlatGL<dimensions>& FlatGL<dimensions>::setPerInstanceJointCount(const UnsignedInt count) {
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
"Shaders::FlatGL::setPerInstanceJointCount(): the shader was created with uniform buffers enabled", *this);
setUniform(_perInstanceJointCountUniform, count);
return *this;
}
#endif
#ifndef MAGNUM_TARGET_GLES2
@ -475,6 +594,20 @@ template<UnsignedInt dimensions> FlatGL<dimensions>& FlatGL<dimensions>::bindMat
buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size);
return *this;
}
template<UnsignedInt dimensions> FlatGL<dimensions>& FlatGL<dimensions>::bindJointBuffer(GL::Buffer& buffer) {
CORRADE_ASSERT(_flags >= Flag::UniformBuffers,
"Shaders::FlatGL::bindJointBuffer(): the shader was not created with uniform buffers enabled", *this);
buffer.bind(GL::Buffer::Target::Uniform, JointBufferBinding);
return *this;
}
template<UnsignedInt dimensions> FlatGL<dimensions>& FlatGL<dimensions>::bindJointBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) {
CORRADE_ASSERT(_flags >= Flag::UniformBuffers,
"Shaders::FlatGL::bindJointBuffer(): the shader was not created with uniform buffers enabled", *this);
buffer.bind(GL::Buffer::Target::Uniform, JointBufferBinding, offset, size);
return *this;
}
#endif
template<UnsignedInt dimensions> FlatGL<dimensions>& FlatGL<dimensions>::bindTexture(GL::Texture2D& texture) {
@ -521,6 +654,21 @@ template<UnsignedInt dimensions> FlatGL<dimensions>& FlatGL<dimensions>::bindObj
}
#endif
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> typename FlatGL<dimensions>::Configuration& FlatGL<dimensions>::Configuration::setJointCount(UnsignedInt count, UnsignedInt perVertexCount, UnsignedInt secondaryPerVertexCount) {
CORRADE_ASSERT(perVertexCount <= 4,
"Shaders::FlatGL::Configuration::setJointCount(): expected at most 4 per-vertex joints, got" << perVertexCount, *this);
CORRADE_ASSERT(secondaryPerVertexCount <= 4,
"Shaders::FlatGL::Configuration::setJointCount(): expected at most 4 secondary per-vertex joints, got" << secondaryPerVertexCount, *this);
CORRADE_ASSERT(!count == (!perVertexCount && !secondaryPerVertexCount),
"Shaders::FlatGL::Configuration::setJointCount(): either both joint count and (secondary) per-vertex joint count has to be non-zero, or all zero", *this);
_jointCount = count;
_perVertexJointCount = perVertexCount;
_secondaryPerVertexJointCount = secondaryPerVertexCount;
return *this;
}
#endif
template class MAGNUM_SHADERS_EXPORT FlatGL<2>;
template class MAGNUM_SHADERS_EXPORT FlatGL<3>;
@ -555,6 +703,7 @@ Debug& operator<<(Debug& debug, const FlatGLFlag value) {
_c(UniformBuffers)
_c(MultiDraw)
_c(TextureArrays)
_c(DynamicPerVertexJointCount)
#endif
#undef _c
/* LCOV_EXCL_STOP */
@ -583,7 +732,8 @@ Debug& operator<<(Debug& debug, const FlatGLFlags value) {
#ifndef MAGNUM_TARGET_GLES2
FlatGLFlag::MultiDraw, /* Superset of UniformBuffers */
FlatGLFlag::UniformBuffers,
FlatGLFlag::TextureArrays
FlatGLFlag::TextureArrays,
FlatGLFlag::DynamicPerVertexJointCount,
#endif
});
}

371
src/Magnum/Shaders/FlatGL.h

@ -55,7 +55,8 @@ namespace Implementation {
#ifndef MAGNUM_TARGET_GLES2
UniformBuffers = 1 << 8,
MultiDraw = UniformBuffers|(1 << 9),
TextureArrays = 1 << 10
TextureArrays = 1 << 10,
DynamicPerVertexJointCount = 1 << 12
#endif
};
typedef Containers::EnumSet<FlatGLFlag> FlatGLFlags;
@ -142,6 +143,26 @@ will contain a sum of the per-vertex ID, texture ID and ID coming from
@requires_webgl20 Object ID output requires integer support in shaders, which
is not available in WebGL 1.0.
@section Shaders-FlatGL-skinning Skinning
To render skinned meshes, bind up to two sets of up to four-component joint ID
and weight attributes to @ref JointIds / @ref SecondaryJointIds and
@ref Weights / @ref SecondaryWeights, set an appropriate joint count and
per-vertex primary and secondary joint count in
@ref Configuration::setJointCount() and upload appropriate joint matrices with
@ref setJointMatrices().
To avoid having to compile multiple shader variants for different per-vertex
joint counts, enable @ref Flag::DynamicPerVertexJointCount, set the maximum
per-vertex joint count in @ref Configuration::setJointCount() and then adjust
the actual per-draw joint count with @ref setPerVertexJointCount().
@requires_gl30 Extension @gl_extension{EXT,texture_integer}
@requires_gles30 Skinning requires integer support in shaders, which is not
available in OpenGL ES 2.0.
@requires_webgl20 Skinning requires integer support in shaders, which is not
available in WebGL 1.0.
@section Shaders-FlatGL-instancing Instanced rendering
Enabling @ref Flag::InstancedTransformation will turn the shader into an
@ -157,6 +178,12 @@ color to a mesh:
@snippet MagnumShaders-gl.cpp FlatGL-usage-instancing
For instanced skinning the joint buffer is assumed to contain joint
transformations for all instances. By default all instances use the same joint
transformations, seting @ref setPerInstanceJointCount() will cause the shader
to offset the per-vertex joint IDs with
@glsl gl_InstanceID*perInstanceJointCount @ce.
@requires_gl33 Extension @gl_extension{ARB,instanced_arrays}
@requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays},
@gl_extension{EXT,instanced_arrays} or @gl_extension{NV,instanced_arrays}
@ -189,6 +216,17 @@ For a multidraw workflow enable @ref Flag::MultiDraw (and possibly
texture offsets/layers for every draw. The usage is similar for all shaders,
see @ref shaders-usage-multidraw for an example.
For skinning, joint matrices are supplied via a @ref TransformationUniform2D /
@ref TransformationUniform3D buffer bound with @ref bindJointBuffer(). In an
instanced scenario the per-instance joint count is supplied via
@ref FlatDrawUniform::perInstanceJointCount, a per-draw joint offset for the
multidraw scenario is supplied via @ref FlatDrawUniform::jointOffset.
Altogether for a particular draw, each per-vertex joint ID is offset with
@glsl gl_InstanceID*perInstanceJointCount + jointOffset @ce. The
@ref setPerVertexJointCount() stays as an immediate uniform in the UBO and
multidraw scenario as well, as it is tied to a particular mesh layout and thus
doesn't need to vary per draw.
@requires_gl30 Extension @gl_extension{EXT,texture_array} for texture arrays.
@requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} for uniform
buffers.
@ -207,7 +245,8 @@ see @ref shaders-usage-multidraw for an example.
*/
template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT FlatGL: public GL::AbstractShaderProgram {
public:
class Configuration;
/* MSVC needs dllexport here as well */
class MAGNUM_SHADERS_EXPORT Configuration;
class CompileState;
/**
@ -248,6 +287,64 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT FlatGL: public GL::
*/
typedef typename GenericGL<dimensions>::Color4 Color4;
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Joint ids
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4ui.
* Used only if @ref perVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::JointIds JointIds;
/**
* @brief Weights
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4.
* Used only if @ref perVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::Weights Weights;
/**
* @brief Secondary joint ids
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4ui.
* Used only if @ref secondaryPerVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::SecondaryJointIds SecondaryJointIds;
/**
* @brief Secondary weights
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4.
* Used only if @ref secondaryPerVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::SecondaryWeights SecondaryWeights;
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief (Instanced) object ID
@ -532,7 +629,30 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT FlatGL: public GL::
* @requires_webgl20 Texture arrays are not available in WebGL 1.0.
* @m_since_latest
*/
TextureArrays = 1 << 10
TextureArrays = 1 << 10,
/**
* Dynamic per-vertex joint count for skinning. Uses only the first
* M / N primary / secondary components defined by
* @ref setPerVertexJointCount() instead of
* all primary / secondary components defined by
* @ref Configuration::setJointCount() at shader compilation time.
* Useful in order to avoid having a shader permutation defined for
* every possible joint count. Unfortunately it's not possible to
* make use of default values for unspecified input components as
* the last component is always @cpp 1.0 @ce instead of
* @cpp 0.0 @ce, on the other hand dynamically limiting the joint
* count can reduce the time spent executing the vertex shader
* compared to going through the full set of per-vertex joints
* always.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders,
* which is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
* @m_since_latest
*/
DynamicPerVertexJointCount = 1 << 12,
#endif
};
@ -663,6 +783,48 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT FlatGL: public GL::
*/
Flags flags() const { return _flags; }
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Joint count
* @m_since_latest
*
* If @ref Flag::UniformBuffers is not set, this is the number of joint
* matrices accepted by @ref setJointMatrices() / @ref setJointMatrix().
* If @ref Flag::UniformBuffers is set, this is the statically defined
* size of the @ref TransformationUniform2D /
* @ref TransformationUniform3D uniform buffer bound with
* @ref bindJointBuffer().
* @see @ref Configuration::setJointCount()
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt jointCount() const { return _jointCount; }
/**
* @brief Per-vertex joint count
* @m_since_latest
*
* Returns the value set with @ref Configuration::setJointCount(). If
* @ref Flag::DynamicPerVertexJointCount is set, the count can be
* additionally modified per-draw using @ref setPerVertexJointCount().
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt perVertexJointCount() const { return _perVertexJointCount; }
/**
* @brief Secondary per-vertex joint count
* @m_since_latest
*
* Returns the value set with @ref Configuration::setJointCount(). If
* @ref Flag::DynamicPerVertexJointCount is set, the count can be
* additionally modified per-draw using @ref setPerVertexJointCount().
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt secondaryPerVertexJointCount() const { return _secondaryPerVertexJointCount; }
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Material count
@ -695,6 +857,34 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT FlatGL: public GL::
UnsignedInt drawCount() const { return _drawCount; }
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Set dynamic per-vertex skinning joint count
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Allows reducing the count of iterated joints for a particular draw
* call, making it possible to use a single shader with meshes that
* contain different count of per-vertex joints. See
* @ref Flag::DynamicPerVertexJointCount for more information. As the
* joint count is tied to the mesh layout, this is a per-draw-call
* setting even in case of @ref Flag::UniformBuffers instead of being
* a value in @ref FlatDrawUniform. Initial value is the same as
* @ref perVertexJointCount() and @ref secondaryPerVertexJointCount().
*
* Expects that @ref Flag::DynamicPerVertexJointCount is set,
* @p count is not larger than @ref perVertexJointCount() and
* @p secondaryCount not larger than @ref secondaryPerVertexJointCount().
* @see @ref Configuration::setJointCount()
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders, which
* is not available in WebGL 1.0.
*/
FlatGL<dimensions>& setPerVertexJointCount(UnsignedInt count, UnsignedInt secondaryCount = 0);
#endif
/** @{
* @name Uniform setters
*
@ -818,6 +1008,78 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT FlatGL: public GL::
FlatGL<dimensions>& setObjectId(UnsignedInt id);
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Set joint matrices
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Initial values are identity transformations. Expects that the size
* of the @p matrices array is the same as @ref jointCount().
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref TransformationUniform2D::transformationMatrix /
* @ref TransformationUniform3D::transformationMatrix and call
* @ref bindJointBuffer() instead.
* @see @ref setJointMatrix(UnsignedInt, const MatrixTypeFor<dimensions, Float>&)
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
FlatGL<dimensions>& setJointMatrices(const Containers::ArrayView<const MatrixTypeFor<dimensions, Float>> matrices);
/**
* @overload
* @m_since_latest
*/
FlatGL<dimensions>& setJointMatrices(std::initializer_list<MatrixTypeFor<dimensions, Float>> matrices);
/**
* @brief Set joint matrix for given joint
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Unlike @ref setJointMatrices() updates just a single joint matrix.
* Expects that @p id is less than @ref jointCount().
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref TransformationUniform2D::transformationMatrix /
* @ref TransformationUniform3D::transformationMatrix and call
* @ref bindJointBuffer() instead.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
FlatGL<dimensions>& setJointMatrix(UnsignedInt id, const MatrixTypeFor<dimensions, Float>& matrix);
/**
* @brief Set per-instance joint count
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Offset added to joint IDs in the @ref JointIds and
* @ref SecondaryJointIds in instanced draws. Should be less than
* @ref jointCount(). Initial value is @cpp 0 @ce, meaning every
* instance will use the same joint matrices, setting it to a non-zero
* value causes the joint IDs to be interpreted as
* @glsl gl_InstanceID*count + jointId @ce.
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref FlatDrawUniform::perInstanceJointCount and call
* @ref bindDrawBuffer() instead.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
FlatGL<dimensions>& setPerInstanceJointCount(UnsignedInt count);
#endif
/**
* @}
*/
@ -936,6 +1198,25 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT FlatGL: public GL::
*/
FlatGL<dimensions>& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size);
/**
* @brief Bind a joint matrix uniform buffer
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Expects that @ref Flag::UniformBuffers is set. The buffer is
* expected to contain @ref jointCount() instances of
* @ref TransformationUniform2D / @ref TransformationUniform3D.
* @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object}
* @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0.
* @requires_webgl20 Uniform buffers are not available in WebGL 1.0.
*/
FlatGL<dimensions>& bindJointBuffer(GL::Buffer& buffer);
/**
* @overload
* @m_since_latest
*/
FlatGL<dimensions>& bindJointBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size);
/**
* @}
*/
@ -1036,7 +1317,11 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT FlatGL: public GL::
Flags _flags;
#ifndef MAGNUM_TARGET_GLES2
UnsignedInt _materialCount{}, _drawCount{};
UnsignedInt _jointCount{},
_perVertexJointCount{},
_secondaryPerVertexJointCount{},
_materialCount{},
_drawCount{};
#endif
Int _transformationProjectionMatrixUniform{0},
_textureMatrixUniform{1},
@ -1046,10 +1331,13 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT FlatGL: public GL::
_colorUniform{3},
_alphaMaskUniform{4};
#ifndef MAGNUM_TARGET_GLES2
Int _objectIdUniform{5};
/* Used instead of all other uniforms when Flag::UniformBuffers is set,
so it can alias them */
Int _drawOffsetUniform{0};
Int _objectIdUniform{5},
_jointMatricesUniform{6},
_perInstanceJointCountUniform, /* 6 + jointCount */
/* Used instead of all other uniforms when Flag::UniformBuffers is
set, so it can alias them */
_drawOffsetUniform{0},
_perVertexJointCountUniform; /* 7 + jointCount, or 1 with UBOs */
#endif
};
@ -1059,7 +1347,7 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT FlatGL: public GL::
@see @ref FlatGL(const Configuration&), @ref compile(const Configuration&)
*/
template<UnsignedInt dimensions> class FlatGL<dimensions>::Configuration {
template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT FlatGL<dimensions>::Configuration {
public:
explicit Configuration() = default;
@ -1078,6 +1366,66 @@ template<UnsignedInt dimensions> class FlatGL<dimensions>::Configuration {
}
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Joint count
*
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt jointCount() const { return _jointCount; }
/**
* @brief Per-vertex joint count
*
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt perVertexJointCount() const { return _perVertexJointCount; }
/**
*@brief Secondary per-vertex joint count
*
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt secondaryPerVertexJointCount() const { return _secondaryPerVertexJointCount; }
/**
* @brief Set joint count
*
* If @ref Flag::UniformBuffers isn't set, @p count describes how many
* joint matrices get supplied to each draw by @ref setJointMatrices()
* / @ref setJointMatrix(). If @ref Flag::UniformBuffers is set,
* @p count describes size of a @ref TransformationUniform2D /
* @ref TransformationUniform3D buffer bound with
* @ref bindJointBuffer(); as uniform buffers are required to have a
* statically defined size. The per-vertex joints then index into the
* array offset by @ref FlatDrawUniform::jointOffset. If @p count is
* @cpp 0 @ce, skinning is not performed.
*
* The @p perVertexCount and @p secondaryPerVertexCount then describe
* how many components are taken from @ref JointIds / @ref Weights and
* @ref SecondaryJointIds / @ref SecondaryWeights attributes. Both
* values are expected to not be larger than @cpp 4 @ce, setting either
* of these to @cpp 0 @ce means given attribute is not used at all. If
* @p count is @cpp 0 @ce, both @p perVertexCount and
* @p secondaryPerVertexCount is expected to be @cpp 0 @ce as well; if
* @p count is non-zero at least one of @p perVertexCount and
* @p secondaryPerVertexCount is expected to be non-zero as well.
*
* Default value for all three is @cpp 0 @ce.
* @see @ref FlatGL::jointCount(), @ref FlatGL::perVertexJointCount(),
* @ref FlatGL::secondaryPerVertexJointCount(),
* @ref Flag::DynamicPerVertexJointCount,
* @ref FlatGL::setPerVertexJointCount()
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
Configuration& setJointCount(UnsignedInt count, UnsignedInt perVertexCount, UnsignedInt secondaryPerVertexCount = 0);
/**
* @brief Material count
*
@ -1144,7 +1492,10 @@ template<UnsignedInt dimensions> class FlatGL<dimensions>::Configuration {
private:
Flags _flags;
#ifndef MAGNUM_TARGET_GLES2
UnsignedInt _materialCount = 1,
UnsignedInt _jointCount = 0,
_perVertexJointCount = 0,
_secondaryPerVertexJointCount = 0,
_materialCount = 1,
_drawCount = 1;
#endif
};

7
src/Magnum/Shaders/MeshVisualizer.frag

@ -144,9 +144,10 @@ struct DrawUniform {
#elif !defined(TWO_DIMENSIONS)
#error
#endif
highp uvec4 materialIdReservedObjectIdReservedReserved;
#define draw_materialIdReserved materialIdReservedObjectIdReservedReserved.x
#define draw_objectId materialIdReservedObjectIdReservedReserved.y
highp uvec4 materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved;
#define draw_materialIdReserved materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved.x
#define draw_objectId materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved.y
#define draw_jointOffsetPerInstanceJointCount materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved.z
};
layout(std140

7
src/Magnum/Shaders/MeshVisualizer.geom

@ -108,9 +108,10 @@ struct DrawUniform {
#elif !defined(TWO_DIMENSIONS)
#error
#endif
highp uvec4 materialIdReservedObjectIdReservedReserved;
#define draw_materialIdReserved materialIdReservedObjectIdReservedReserved.x
#define draw_objectId materialIdReservedObjectIdReservedReserved.y
highp uvec4 materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved;
#define draw_materialIdReserved materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved.x
#define draw_objectId materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved.y
#define draw_jointOffsetPerInstanceJointCount materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved.z
};
layout(std140

128
src/Magnum/Shaders/MeshVisualizer.h

@ -54,7 +54,13 @@ separate @ref MeshVisualizerMaterialUniform structure, referenced by
*/
struct MeshVisualizerDrawUniform2D {
/** @brief Construct with default parameters */
constexpr explicit MeshVisualizerDrawUniform2D(DefaultInitT = DefaultInit) noexcept: materialId{0}, objectId{0} {}
constexpr explicit MeshVisualizerDrawUniform2D(DefaultInitT = DefaultInit) noexcept: materialId{0}, objectId{0},
#ifndef CORRADE_TARGET_BIG_ENDIAN
jointOffset{0}, perInstanceJointCount{0}
#else
perInstanceJointCount{0}, jointOffset{0}
#endif
{}
/** @brief Construct without initializing the contents */
explicit MeshVisualizerDrawUniform2D(NoInitT) noexcept {}
@ -86,6 +92,24 @@ struct MeshVisualizerDrawUniform2D {
return *this;
}
/**
* @brief Set the @ref jointOffset field
* @return Reference to self (for method chaining)
*/
MeshVisualizerDrawUniform2D& setJointOffset(UnsignedInt offset) {
jointOffset = offset;
return *this;
}
/**
* @brief Set the @ref perInstanceJointCount field
* @return Reference to self (for method chaining)
*/
MeshVisualizerDrawUniform2D& setPerInstanceJointCount(UnsignedInt count) {
perInstanceJointCount = count;
return *this;
}
/**
* @}
*/
@ -111,10 +135,10 @@ struct MeshVisualizerDrawUniform2D {
/* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK
I MADE THOSE UNNAMED, YOU DUMB FOOL */
#ifndef DOXYGEN_GENERATING_OUTPUT
UnsignedShort:16; /* reserved for skinOffset */
UnsignedShort:16; /* reserved */
#endif
#else
UnsignedShort:16; /* reserved for skinOffset */
UnsignedShort:16; /* reserved */
UnsignedShort materialId;
#endif
@ -133,11 +157,43 @@ struct MeshVisualizerDrawUniform2D {
*/
UnsignedInt objectId;
/** @var jointOffset
* @brief Joint offset
*
* Offset added to joint IDs in the @ref MeshVisualizerGL2D::JointIds and
* @ref MeshVisualizerGL2D::SecondaryJointIds attributes. Useful when a UBO
* with joint matrices for more than one skin is supplied or in a
* multi-draw scenario. Should be less than the joint count passed to
* @ref MeshVisualizerGL2D::Configuration::setJointCount(). Default value
* is @cpp 0 @ce, meaning no offset is added to joint IDs.
*/
/** @var perInstanceJointCount
* @brief Per-instance joint count
*
* Offset added to joint IDs in the @ref MeshVisualizerGL2D::JointIds and
* @ref MeshVisualizerGL2D::SecondaryJointIds atttributes in instanced
* draws. Should be less than the joint count passed to
* @ref MeshVisualizerGL2D::Configuration::setJointCount(). Default value
* is @cpp 0 @ce, meaning every instance will use the same joint matrices,
* setting it to a non-zero value causes the joint IDs to be interpreted as
* @glsl gl_InstanceID*count + jointId @ce.
*/
/* This field is an UnsignedInt in the shader and jointOffset is extracted
as (value & 0xffff), so the order has to be different on BE */
#ifndef CORRADE_TARGET_BIG_ENDIAN
UnsignedShort jointOffset;
UnsignedShort perInstanceJointCount;
#else
UnsignedShort perInstanceJointCount;
UnsignedShort jointOffset;
#endif
/* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK
I MADE THOSE UNNAMED, YOU DUMB FOOL */
#ifndef DOXYGEN_GENERATING_OUTPUT
Int:32;
Int:32;
#endif
};
@ -153,7 +209,13 @@ shared among multiple draw calls and thus are provided in a separate
*/
struct MeshVisualizerDrawUniform3D {
/** @brief Construct with default parameters */
constexpr explicit MeshVisualizerDrawUniform3D(DefaultInitT = DefaultInit) noexcept: normalMatrix{Math::IdentityInit}, materialId{0}, objectId{0} {}
constexpr explicit MeshVisualizerDrawUniform3D(DefaultInitT = DefaultInit) noexcept: normalMatrix{Math::IdentityInit}, materialId{0}, objectId{0},
#ifndef CORRADE_TARGET_BIG_ENDIAN
jointOffset{0}, perInstanceJointCount{0}
#else
perInstanceJointCount{0}, jointOffset{0}
#endif
{}
/** @brief Construct without initializing the contents */
explicit MeshVisualizerDrawUniform3D(NoInitT) noexcept: normalMatrix{NoInit} {}
@ -197,6 +259,24 @@ struct MeshVisualizerDrawUniform3D {
return *this;
}
/**
* @brief Set the @ref jointOffset field
* @return Reference to self (for method chaining)
*/
MeshVisualizerDrawUniform3D& setJointOffset(UnsignedInt offset) {
jointOffset = offset;
return *this;
}
/**
* @brief Set the @ref perInstanceJointCount field
* @return Reference to self (for method chaining)
*/
MeshVisualizerDrawUniform3D& setPerInstanceJointCount(UnsignedInt count) {
perInstanceJointCount = count;
return *this;
}
/**
* @}
*/
@ -228,10 +308,10 @@ struct MeshVisualizerDrawUniform3D {
/* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK
I MADE THOSE UNNAMED, YOU DUMB FOOL */
#ifndef DOXYGEN_GENERATING_OUTPUT
UnsignedShort:16; /* reserved for skinOffset */
UnsignedShort:16; /* reserved */
#endif
#else
UnsignedShort:16; /* reserved for skinOffset */
UnsignedShort:16; /* reserved */
UnsignedShort materialId;
#endif
@ -250,11 +330,43 @@ struct MeshVisualizerDrawUniform3D {
*/
UnsignedInt objectId;
/** @var jointOffset
* @brief Joint offset
*
* Offset added to joint IDs in the @ref MeshVisualizerGL2D::JointIds and
* @ref MeshVisualizerGL2D::SecondaryJointIds attributes. Useful when a UBO
* with joint matrices for more than one skin is supplied or in a
* multi-draw scenario. Should be less than the joint count passed to
* @ref MeshVisualizerGL2D::Configuration::setJointCount(). Default value
* is @cpp 0 @ce, meaning no offset is added to joint IDs.
*/
/** @var perInstanceJointCount
* @brief Per-instance joint count
*
* Offset added to joint IDs in the @ref MeshVisualizerGL2D::JointIds and
* @ref MeshVisualizerGL2D::SecondaryJointIds atttributes in instanced
* draws. Should be less than the joint count passed to
* @ref MeshVisualizerGL2D::Configuration::setJointCount(). Default value
* is @cpp 0 @ce, meaning every instance will use the same joint matrices,
* setting it to a non-zero value causes the joint IDs to be interpreted as
* @glsl gl_InstanceID*count + jointId @ce.
*/
/* This field is an UnsignedInt in the shader and jointOffset is extracted
as (value & 0xffff), so the order has to be different on BE */
#ifndef CORRADE_TARGET_BIG_ENDIAN
UnsignedShort jointOffset;
UnsignedShort perInstanceJointCount;
#else
UnsignedShort perInstanceJointCount;
UnsignedShort jointOffset;
#endif
/* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK
I MADE THOSE UNNAMED, YOU DUMB FOOL */
#ifndef DOXYGEN_GENERATING_OUTPUT
Int:32;
Int:32;
#endif
};

141
src/Magnum/Shaders/MeshVisualizer.vert

@ -48,6 +48,19 @@
#define const
#endif
/* Both classic uniforms and uniform buffers */
#ifdef DYNAMIC_PER_VERTEX_JOINT_COUNT
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = PER_VERTEX_JOINT_COUNT_LOCATION)
#endif
uniform mediump uvec2 perVertexJointCount
#ifndef GL_ES
= uvec2(PER_VERTEX_JOINT_COUNT, SECONDARY_PER_VERTEX_JOINT_COUNT)
#endif
;
#endif
/* Uniforms */
#ifndef UNIFORM_BUFFERS
@ -133,6 +146,32 @@ layout(location = 8)
uniform highp uint textureLayer; /* defaults to zero */
#endif
#ifdef JOINT_COUNT
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 14)
#endif
#ifdef TWO_DIMENSIONS
uniform mat3 jointMatrices[JOINT_COUNT]
#ifndef GL_ES
= mat3[](JOINT_MATRIX_INITIALIZER)
#endif
;
#elif defined(THREE_DIMENSIONS)
uniform mat4 jointMatrices[JOINT_COUNT]
#ifndef GL_ES
= mat4[](JOINT_MATRIX_INITIALIZER)
#endif
;
#else
#error
#endif
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = PER_INSTANCE_JOINT_COUNT_LOCATION)
#endif
uniform uint perInstanceJointCount; /* defaults to zero */
#endif
/* Uniform buffers */
#else
@ -206,9 +245,10 @@ struct DrawUniform {
#elif !defined(TWO_DIMENSIONS)
#error
#endif
highp uvec4 materialIdReservedObjectIdReservedReserved;
#define draw_materialIdReserved materialIdReservedObjectIdReservedReserved.x
#define draw_objectId materialIdReservedObjectIdReservedReserved.y
highp uvec4 materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved;
#define draw_materialIdReserved materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved.x
#define draw_objectId materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved.y
#define draw_jointOffsetPerInstanceJointCount materialIdReservedObjectIdJointOffsetPerInstanceJointCountReserved.z
};
layout(std140
@ -242,6 +282,26 @@ layout(std140
) uniform Material {
MaterialUniform materials[MATERIAL_COUNT];
};
#ifdef JOINT_COUNT
layout(std140
#ifdef EXPLICIT_BINDING
, binding = 6
#endif
) uniform Joint {
highp
#ifdef TWO_DIMENSIONS
/* Can't be a mat3 because of ANGLE, see DrawUniform in Phong.vert for
details */
mat3x4
#elif defined(THREE_DIMENSIONS)
mat4
#else
#error
#endif
jointMatrices[JOINT_COUNT];
};
#endif
#endif
/* Inputs */
@ -285,6 +345,32 @@ layout(location = TEXTURECOORDINATES_ATTRIBUTE_LOCATION)
in mediump vec2 textureCoordinates;
#endif
#ifdef JOINT_COUNT
#if PER_VERTEX_JOINT_COUNT
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = WEIGHTS_ATTRIBUTE_LOCATION)
#endif
in mediump vec4 weights;
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = JOINTIDS_ATTRIBUTE_LOCATION)
#endif
in mediump uvec4 jointIds;
#endif
#if SECONDARY_PER_VERTEX_JOINT_COUNT
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = SECONDARY_WEIGHTS_ATTRIBUTE_LOCATION)
#endif
in mediump vec4 secondaryWeights;
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = SECONDARY_JOINTIDS_ATTRIBUTE_LOCATION)
#endif
in mediump uvec4 secondaryJointIds;
#endif
#endif
#ifdef INSTANCED_TEXTURE_OFFSET
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = TEXTURE_OFFSET_ATTRIBUTE_LOCATION)
@ -442,6 +528,49 @@ void main() {
highp const uint textureLayer = floatBitsToUint(textureTransformations[drawId].textureTransformation_layer);
#endif
#endif
#ifdef JOINT_COUNT
mediump const uint jointOffset = (draws[drawId].draw_jointOffsetPerInstanceJointCount & 0xffffu) + uint(gl_InstanceID)*(draws[drawId].draw_jointOffsetPerInstanceJointCount >> 16 & 0xffffu);
#endif
#else
#ifdef JOINT_COUNT
mediump const uint jointOffset = uint(gl_InstanceID)*perInstanceJointCount;
#endif
#endif
#ifdef JOINT_COUNT
#ifdef TWO_DIMENSIONS
mat3 skinMatrix = mat3(0.0);
#elif defined(THREE_DIMENSIONS)
mat4 skinMatrix = mat4(0.0);
#else
#error
#endif
#if PER_VERTEX_JOINT_COUNT
for(uint i = 0u; i != PER_VERTEX_JOINT_COUNT
#ifdef DYNAMIC_PER_VERTEX_JOINT_COUNT
&& i != perVertexJointCount.x
#endif
; ++i)
skinMatrix += weights[i]*
#ifdef TWO_DIMENSIONS
mat3 /* need to slice because it's a mat3x4 due to ANGLE, see
DrawUniform in Phong.vert for details */
#endif
(jointMatrices[jointOffset + jointIds[i]]);
#endif
#if SECONDARY_PER_VERTEX_JOINT_COUNT
for(uint i = 0u; i != SECONDARY_PER_VERTEX_JOINT_COUNT
#ifdef DYNAMIC_PER_VERTEX_JOINT_COUNT
&& i != perVertexJointCount.y
#endif
; ++i)
skinMatrix += secondaryWeights[i]*
#ifdef TWO_DIMENSIONS
mat3 /* need to slice because it's a mat3x4 due to ANGLE, see
DrawUniform in Phong.vert for details */
#endif
(jointMatrices[jointOffset + secondaryJointIds[i]]);
#endif
#endif
#ifdef TWO_DIMENSIONS
@ -449,12 +578,18 @@ void main() {
#ifdef INSTANCED_TRANSFORMATION
instancedTransformationMatrix*
#endif
#ifdef JOINT_COUNT
skinMatrix*
#endif
vec3(position, 1.0), 0.0);
#elif defined(THREE_DIMENSIONS)
highp const vec4 transformedPosition4 = transformationMatrix*
#ifdef INSTANCED_TRANSFORMATION
instancedTransformationMatrix*
#endif
#ifdef JOINT_COUNT
skinMatrix*
#endif
position;
gl_Position = projectionMatrix*transformedPosition4;
#else

284
src/Magnum/Shaders/MeshVisualizerGL.cpp

@ -48,6 +48,8 @@
namespace Magnum { namespace Shaders {
using namespace Containers::Literals;
namespace {
enum: Int {
/* First four taken by Phong (A/D/S/N) */
@ -57,6 +59,9 @@ namespace {
#ifndef MAGNUM_TARGET_GLES2
enum: Int {
/* Projection, transformation, texture transformation and joints is
slots 0, 1, 3, 6 in all shaders so shaders can be switched without
rebinding everything */
ProjectionBufferBinding = 0,
/* Not using the zero binding to avoid conflicts with
ProjectionBufferBinding from the 3D variant which can likely stay
@ -66,6 +71,8 @@ namespace {
DrawBufferBinding = 2,
TextureTransformationBufferBinding = 3,
MaterialBufferBinding = 4,
/* 5 unused */
JointBufferBinding = 6,
};
#endif
}
@ -142,7 +149,11 @@ void MeshVisualizerGLBase::assertExtensions(const FlagsBase flags) {
GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& frag, const Utility::Resource& rs, const FlagsBase flags
#ifndef MAGNUM_TARGET_GLES2
, const UnsignedInt materialCount, UnsignedInt const drawCount
, const UnsignedInt
#ifndef MAGNUM_TARGET_GLES
dimensions /* used for a uniform initializer, which isn't on GLSL ES */
#endif
, const UnsignedInt jointCount, const UnsignedInt perVertexJointCount, const UnsignedInt secondaryPerVertexJointCount, const UnsignedInt materialCount, const UnsignedInt drawCount, const UnsignedInt perInstanceJointCountUniform, const UnsignedInt perVertexJointCountUniform
#endif
) {
GL::Context& context = GL::Context::current();
@ -184,6 +195,31 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra
#endif
;
#ifndef MAGNUM_TARGET_GLES2
if(jointCount) {
vert.addSource(Utility::formatString(
"#define JOINT_COUNT {}\n"
"#define PER_VERTEX_JOINT_COUNT {}u\n"
"#define SECONDARY_PER_VERTEX_JOINT_COUNT {}u\n"
#ifndef MAGNUM_TARGET_GLES
"#define JOINT_MATRIX_INITIALIZER {}\n"
#endif
"#define PER_INSTANCE_JOINT_COUNT_LOCATION {}\n",
jointCount,
perVertexJointCount,
secondaryPerVertexJointCount,
#ifndef MAGNUM_TARGET_GLES
((dimensions == 2 ? "mat3(1.0), "_s : "mat4(1.0), "_s)*jointCount).exceptSuffix(2),
#endif
perInstanceJointCountUniform));
}
if(flags >= FlagBase::DynamicPerVertexJointCount) {
vert.addSource(Utility::formatString(
"#define DYNAMIC_PER_VERTEX_JOINT_COUNT\n"
"#define PER_VERTEX_JOINT_COUNT_LOCATION {}\n",
perVertexJointCountUniform));
}
#endif
#ifndef MAGNUM_TARGET_GLES2
if(flags >= FlagBase::UniformBuffers) {
vert.addSource(Utility::formatString(
"#define UNIFORM_BUFFERS\n"
@ -222,6 +258,19 @@ GL::Version MeshVisualizerGLBase::setupShaders(GL::Shader& vert, GL::Shader& fra
return version;
}
#ifndef MAGNUM_TARGET_GLES2
MeshVisualizerGLBase& MeshVisualizerGLBase::setPerVertexJointCount(const UnsignedInt count, const UnsignedInt secondaryCount) {
CORRADE_ASSERT(_flags >= FlagBase::DynamicPerVertexJointCount,
"Shaders::MeshVisualizerGL::setPerVertexJointCount(): the shader was not created with dynamic per-vertex joint count enabled", *this);
CORRADE_ASSERT(count <= _perVertexJointCount,
"Shaders::MeshVisualizerGL::setPerVertexJointCount(): expected at most" << _perVertexJointCount << "per-vertex joints, got" << count, *this);
CORRADE_ASSERT(secondaryCount <= _secondaryPerVertexJointCount,
"Shaders::MeshVisualizerGL::setPerVertexJointCount(): expected at most" << _secondaryPerVertexJointCount << "secondary per-vertex joints, got" << secondaryCount, *this);
setUniform(_perVertexJointCountUniform, Vector2ui{count, secondaryCount});
return *this;
}
#endif
#ifndef MAGNUM_TARGET_GLES2
MeshVisualizerGLBase& MeshVisualizerGLBase::setTextureMatrix(const Matrix3& matrix) {
#ifndef MAGNUM_TARGET_GLES2
@ -300,6 +349,13 @@ MeshVisualizerGLBase& MeshVisualizerGLBase::setColorMapTransformation(const Floa
setUniform(_colorMapOffsetScaleUniform, Vector2{offset, scale});
return *this;
}
MeshVisualizerGLBase& MeshVisualizerGLBase::setPerInstanceJointCount(const UnsignedInt count) {
CORRADE_ASSERT(!(_flags >= FlagBase::UniformBuffers),
"Shaders::MeshVisualizerGL::setPerInstanceJointCount(): the shader was created with uniform buffers enabled", *this);
setUniform(_perInstanceJointCountUniform, count);
return *this;
}
#endif
#ifndef MAGNUM_TARGET_GLES2
@ -343,6 +399,20 @@ MeshVisualizerGLBase& MeshVisualizerGLBase::bindMaterialBuffer(GL::Buffer& buffe
buffer.bind(GL::Buffer::Target::Uniform, MaterialBufferBinding, offset, size);
return *this;
}
MeshVisualizerGLBase& MeshVisualizerGLBase::bindJointBuffer(GL::Buffer& buffer) {
CORRADE_ASSERT(_flags >= FlagBase::UniformBuffers,
"Shaders::MeshVisualizerGL::bindJointBuffer(): the shader was not created with uniform buffers enabled", *this);
buffer.bind(GL::Buffer::Target::Uniform, JointBufferBinding);
return *this;
}
MeshVisualizerGLBase& MeshVisualizerGLBase::bindJointBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) {
CORRADE_ASSERT(_flags >= FlagBase::UniformBuffers,
"Shaders::MeshVisualizerGL::bindJointBuffer(): the shader was not created with uniform buffers enabled", *this);
buffer.bind(GL::Buffer::Target::Uniform, JointBufferBinding, offset, size);
return *this;
}
#endif
#ifndef MAGNUM_TARGET_GLES2
@ -398,12 +468,35 @@ MeshVisualizerGL2D::CompileState MeshVisualizerGL2D::compile(const Configuration
"Shaders::MeshVisualizerGL2D: draw count can't be zero", CompileState{NoCreate});
#endif
/* Has to be here and not in the base class in order to have it exit the
constructor when testing for asserts -- GLSL compilation would fail
otherwise */
#ifndef MAGNUM_TARGET_GLES2
CORRADE_ASSERT(!(configuration.flags() & Flag::DynamicPerVertexJointCount) || configuration.jointCount(),
"Shaders::MeshVisualizerGL2D: dynamic per-vertex joint count enabled for zero joints", CompileState{NoCreate});
CORRADE_ASSERT(!(configuration.flags() & Flag::InstancedTransformation) || !configuration.secondaryPerVertexJointCount(),
"Shaders::MeshVisualizerGL2D: TransformationMatrix attribute binding conflicts with the SecondaryJointIds / SecondaryWeights attributes, use a non-instanced rendering with secondary weights instead", CompileState{NoCreate});
#endif
MeshVisualizerGL2D out{NoInit};
out._flags = baseFlags;
#ifndef MAGNUM_TARGET_GLES2
out._jointCount = configuration.jointCount();
out._perVertexJointCount = configuration.perVertexJointCount();
out._secondaryPerVertexJointCount = configuration.secondaryPerVertexJointCount();
out._materialCount = configuration.materialCount();
out._drawCount = configuration.drawCount();
out._perInstanceJointCountUniform = out._jointMatricesUniform + configuration.jointCount();
out._perVertexJointCountUniform = configuration.flags() >= Flag::UniformBuffers ?
2 : out._perInstanceJointCountUniform + 1;
#endif
Utility::Resource rs{"MagnumShadersGL"};
GL::Shader vert{NoCreate};
GL::Shader frag{NoCreate};
const GL::Version version = setupShaders(vert, frag, rs, baseFlags
#ifndef MAGNUM_TARGET_GLES2
, configuration.materialCount(), configuration.drawCount()
, 2, configuration.jointCount(), configuration.perVertexJointCount(), configuration.secondaryPerVertexJointCount(), configuration.materialCount(), configuration.drawCount(), out._perInstanceJointCountUniform, out._perVertexJointCountUniform
#endif
);
Containers::Optional<GL::Shader> geom;
@ -466,13 +559,6 @@ MeshVisualizerGL2D::CompileState MeshVisualizerGL2D::compile(const Configuration
frag.submitCompile();
if(geom) geom->submitCompile();
MeshVisualizerGL2D out{NoInit};
out._flags = baseFlags;
#ifndef MAGNUM_TARGET_GLES2
out._materialCount = configuration.materialCount();
out._drawCount = configuration.drawCount();
#endif
out.attachShaders({vert, frag});
if(geom) out.attachShader(*geom);
@ -504,6 +590,19 @@ MeshVisualizerGL2D::CompileState MeshVisualizerGL2D::compile(const Configuration
out.bindAttributeLocation(VertexIndex::Location, "vertexIndex");
}
#endif
#ifndef MAGNUM_TARGET_GLES2
/* Configuration::setJointCount() checks that jointCount and
perVertexJointCount / secondaryPerVertexJointCount are either all
zero or non-zero so we don't need to check for jointCount() here */
if(configuration.perVertexJointCount()) {
out.bindAttributeLocation(Weights::Location, "weights");
out.bindAttributeLocation(JointIds::Location, "jointIds");
}
if(configuration.secondaryPerVertexJointCount()) {
out.bindAttributeLocation(SecondaryWeights::Location, "secondaryWeights");
out.bindAttributeLocation(SecondaryJointIds::Location, "secondaryJointIds");
}
#endif
}
#endif
@ -551,6 +650,8 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(CompileState&& state): MeshVisualizerGL2D
_viewportSizeUniform = uniformLocation("viewportSize");
#ifndef MAGNUM_TARGET_GLES2
if(flags() >= Flag::DynamicPerVertexJointCount)
_perVertexJointCountUniform = uniformLocation("perVertexJointCount");
if(flags() >= Flag::UniformBuffers) {
if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset");
} else
@ -581,6 +682,12 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(CompileState&& state): MeshVisualizerGL2D
if(flags() & Flag::ObjectId)
_objectIdUniform = uniformLocation("objectId");
#endif
#ifndef MAGNUM_TARGET_GLES2
if(_jointCount) {
_jointMatricesUniform = uniformLocation("jointMatrices");
_perInstanceJointCountUniform = uniformLocation("perInstanceJointCount");
}
#endif
}
}
@ -601,6 +708,8 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(CompileState&& state): MeshVisualizerGL2D
setUniformBlockBinding(uniformBlockIndex("Material"), MaterialBufferBinding);
if(flags() & Flag::TextureTransformation)
setUniformBlockBinding(uniformBlockIndex("TextureTransformation"), TextureTransformationBufferBinding);
if(_jointCount)
setUniformBlockBinding(uniformBlockIndex("Joint"), JointBufferBinding);
}
#endif
}
@ -609,6 +718,10 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(CompileState&& state): MeshVisualizerGL2D
/* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */
#ifdef MAGNUM_TARGET_GLES
#ifndef MAGNUM_TARGET_GLES2
if(flags() >= Flag::DynamicPerVertexJointCount)
setPerVertexJointCount(_perVertexJointCount, _secondaryPerVertexJointCount);
#endif
#ifndef MAGNUM_TARGET_GLES2
if(flags() >= Flag::UniformBuffers) {
/* Viewport size is zero by default */
/* Draw offset is zero by default */
@ -632,6 +745,12 @@ MeshVisualizerGL2D::MeshVisualizerGL2D(CompileState&& state): MeshVisualizerGL2D
if(flags() & (Flag::ObjectId|Flag::VertexId|Flag::PrimitiveIdFromVertexId))
setColorMapTransformation(1.0f/512.0f, 1.0f/256.0f);
#endif
#ifndef MAGNUM_TARGET_GLES2
if(_jointCount) {
setJointMatrices(Containers::Array<Matrix3>{DirectInit, _jointCount, Math::IdentityInit});
/* Per-instance joint count is zero by default */
}
#endif
}
#endif
}
@ -680,6 +799,30 @@ MeshVisualizerGL2D& MeshVisualizerGL2D::setSmoothness(const Float smoothness) {
return *this;
}
#ifndef MAGNUM_TARGET_GLES2
MeshVisualizerGL2D& MeshVisualizerGL2D::setJointMatrices(const Containers::ArrayView<const Matrix3> matrices) {
CORRADE_ASSERT(!(flags() >= Flag::UniformBuffers),
"Shaders::MeshVisualizerGL2D::setJointMatrices(): the shader was created with uniform buffers enabled", *this);
CORRADE_ASSERT(_jointCount == matrices.size(),
"Shaders::MeshVisualizerGL2D::setJointMatrices(): expected" << _jointCount << "items but got" << matrices.size(), *this);
if(_jointCount) setUniform(_jointMatricesUniform, matrices);
return *this;
}
MeshVisualizerGL2D& MeshVisualizerGL2D::setJointMatrices(const std::initializer_list<Matrix3> matrices) {
return setJointMatrices(Containers::arrayView(matrices));
}
MeshVisualizerGL2D& MeshVisualizerGL2D::setJointMatrix(const UnsignedInt id, const Matrix3& matrix) {
CORRADE_ASSERT(!(flags() >= Flag::UniformBuffers),
"Shaders::MeshVisualizerGL2D::setJointMatrix(): the shader was created with uniform buffers enabled", *this);
CORRADE_ASSERT(id < _jointCount,
"Shaders::MeshVisualizerGL2D::setJointMatrix(): joint ID" << id << "is out of bounds for" << _jointCount << "joints", *this);
setUniform(_jointMatricesUniform + id, matrix);
return *this;
}
#endif
#ifndef MAGNUM_TARGET_GLES2
MeshVisualizerGL2D& MeshVisualizerGL2D::bindTransformationProjectionBuffer(GL::Buffer& buffer) {
CORRADE_ASSERT(flags() >= Flag::UniformBuffers,
@ -710,6 +853,21 @@ MeshVisualizerGL2D& MeshVisualizerGL2D::bindDrawBuffer(GL::Buffer& buffer, const
}
#endif
#ifndef MAGNUM_TARGET_GLES2
MeshVisualizerGL2D::Configuration& MeshVisualizerGL2D::Configuration::setJointCount(UnsignedInt count, UnsignedInt perVertexCount, UnsignedInt secondaryPerVertexCount) {
CORRADE_ASSERT(perVertexCount <= 4,
"Shaders::MeshVisualizerGL2D::Configuration::setJointCount(): expected at most 4 per-vertex joints, got" << perVertexCount, *this);
CORRADE_ASSERT(secondaryPerVertexCount <= 4,
"Shaders::MeshVisualizerGL2D::Configuration::setJointCount(): expected at most 4 secondary per-vertex joints, got" << secondaryPerVertexCount, *this);
CORRADE_ASSERT(!count == (!perVertexCount && !secondaryPerVertexCount),
"Shaders::MeshVisualizerGL2D::Configuration::setJointCount(): either both joint count and (secondary) per-vertex joint count has to be non-zero, or all zero", *this);
_jointCount = count;
_perVertexJointCount = perVertexCount;
_secondaryPerVertexJointCount = secondaryPerVertexCount;
return *this;
}
#endif
MeshVisualizerGL3D::CompileState MeshVisualizerGL3D::compile(const Configuration& configuration) {
FlagsBase baseFlags = Implementation::MeshVisualizerGLBase::FlagBase(UnsignedInt(configuration.flags()));
assertExtensions(baseFlags);
@ -744,12 +902,35 @@ MeshVisualizerGL3D::CompileState MeshVisualizerGL3D::compile(const Configuration
"Shaders::MeshVisualizerGL3D: draw count can't be zero", CompileState{NoCreate});
#endif
/* Has to be here and not in the base class in order to have it exit the
constructor when testing for asserts -- GLSL compilation would fail
otherwise */
#ifndef MAGNUM_TARGET_GLES2
CORRADE_ASSERT(!(configuration.flags() & Flag::DynamicPerVertexJointCount) || configuration.jointCount(),
"Shaders::MeshVisualizerGL3D: dynamic per-vertex joint count enabled for zero joints", CompileState{NoCreate});
CORRADE_ASSERT(!(configuration.flags() & Flag::InstancedTransformation) || !configuration.secondaryPerVertexJointCount(),
"Shaders::MeshVisualizerGL3D: TransformationMatrix attribute binding conflicts with the SecondaryJointIds / SecondaryWeights attributes, use a non-instanced rendering with secondary weights instead", CompileState{NoCreate});
#endif
MeshVisualizerGL3D out{NoInit};
out._flags = baseFlags;
#ifndef MAGNUM_TARGET_GLES2
out._jointCount = configuration.jointCount();
out._perVertexJointCount = configuration.perVertexJointCount();
out._secondaryPerVertexJointCount = configuration.secondaryPerVertexJointCount();
out._materialCount = configuration.materialCount();
out._drawCount = configuration.drawCount();
out._perInstanceJointCountUniform = out._jointMatricesUniform + configuration.jointCount();
out._perVertexJointCountUniform = configuration.flags() >= Flag::UniformBuffers ?
2 : out._perInstanceJointCountUniform + 1;
#endif
Utility::Resource rs{"MagnumShadersGL"};
GL::Shader vert{NoCreate};
GL::Shader frag{NoCreate};
const GL::Version version = setupShaders(vert, frag, rs, baseFlags
#ifndef MAGNUM_TARGET_GLES2
, configuration.materialCount(), configuration.drawCount()
, 3, configuration.jointCount(), configuration.perVertexJointCount(), configuration.secondaryPerVertexJointCount(), configuration.materialCount(), configuration.drawCount(), out._perInstanceJointCountUniform, out._perVertexJointCountUniform
#endif
);
Containers::Optional<GL::Shader> geom;
@ -846,13 +1027,6 @@ MeshVisualizerGL3D::CompileState MeshVisualizerGL3D::compile(const Configuration
frag.submitCompile();
if(geom) geom->submitCompile();
MeshVisualizerGL3D out{NoInit};
out._flags = baseFlags;
#ifndef MAGNUM_TARGET_GLES2
out._materialCount = configuration.materialCount();
out._drawCount = configuration.drawCount();
#endif
out.attachShaders({vert, frag});
if(geom) out.attachShader(*geom);
@ -899,6 +1073,19 @@ MeshVisualizerGL3D::CompileState MeshVisualizerGL3D::compile(const Configuration
out.bindAttributeLocation(VertexIndex::Location, "vertexIndex");
}
#endif
#ifndef MAGNUM_TARGET_GLES2
/* Configuration::setJointCount() checks that jointCount and
perVertexJointCount / secondaryPerVertexJointCount are either all
zero or non-zero so we don't need to check for jointCount() here */
if(configuration.perVertexJointCount()) {
out.bindAttributeLocation(Weights::Location, "weights");
out.bindAttributeLocation(JointIds::Location, "jointIds");
}
if(configuration.secondaryPerVertexJointCount()) {
out.bindAttributeLocation(SecondaryWeights::Location, "secondaryWeights");
out.bindAttributeLocation(SecondaryJointIds::Location, "secondaryJointIds");
}
#endif
}
#endif
@ -950,6 +1137,8 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(CompileState&& state): MeshVisualizerGL3D
_viewportSizeUniform = uniformLocation("viewportSize");
#ifndef MAGNUM_TARGET_GLES2
if(flags() >= Flag::DynamicPerVertexJointCount)
_perVertexJointCountUniform = uniformLocation("perVertexJointCount");
if(flags() >= Flag::UniformBuffers) {
if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset");
} else
@ -994,6 +1183,12 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(CompileState&& state): MeshVisualizerGL3D
_lineLengthUniform = uniformLocation("lineLength");
}
#endif
#ifndef MAGNUM_TARGET_GLES2
if(_jointCount) {
_jointMatricesUniform = uniformLocation("jointMatrices");
_perInstanceJointCountUniform = uniformLocation("perInstanceJointCount");
}
#endif
}
}
@ -1015,6 +1210,8 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(CompileState&& state): MeshVisualizerGL3D
setUniformBlockBinding(uniformBlockIndex("Material"), MaterialBufferBinding);
if(flags() & Flag::TextureTransformation)
setUniformBlockBinding(uniformBlockIndex("TextureTransformation"), TextureTransformationBufferBinding);
if(_jointCount)
setUniformBlockBinding(uniformBlockIndex("Joint"), JointBufferBinding);
}
#endif
}
@ -1023,6 +1220,10 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(CompileState&& state): MeshVisualizerGL3D
/* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */
#ifdef MAGNUM_TARGET_GLES
#ifndef MAGNUM_TARGET_GLES2
if(flags() >= Flag::DynamicPerVertexJointCount)
setPerVertexJointCount(_perVertexJointCount, _secondaryPerVertexJointCount);
#endif
#ifndef MAGNUM_TARGET_GLES2
if(flags() >= Flag::UniformBuffers) {
/* Viewport size is zero by default */
/* Draw offset is zero by default */
@ -1060,6 +1261,12 @@ MeshVisualizerGL3D::MeshVisualizerGL3D(CompileState&& state): MeshVisualizerGL3D
setLineLength(1.0f);
}
#endif
#ifndef MAGNUM_TARGET_GLES2
if(_jointCount) {
setJointMatrices(Containers::Array<Matrix4>{DirectInit, _jointCount, Math::IdentityInit});
/* Per-instance joint count is zero by default */
}
#endif
}
#endif
}
@ -1161,6 +1368,30 @@ MeshVisualizerGL3D& MeshVisualizerGL3D::setSmoothness(const Float smoothness) {
return *this;
}
#ifndef MAGNUM_TARGET_GLES2
MeshVisualizerGL3D& MeshVisualizerGL3D::setJointMatrices(const Containers::ArrayView<const Matrix4> matrices) {
CORRADE_ASSERT(!(flags() >= Flag::UniformBuffers),
"Shaders::MeshVisualizerGL3D::setJointMatrices(): the shader was created with uniform buffers enabled", *this);
CORRADE_ASSERT(_jointCount == matrices.size(),
"Shaders::MeshVisualizerGL3D::setJointMatrices(): expected" << _jointCount << "items but got" << matrices.size(), *this);
if(_jointCount) setUniform(_jointMatricesUniform, matrices);
return *this;
}
MeshVisualizerGL3D& MeshVisualizerGL3D::setJointMatrices(const std::initializer_list<Matrix4> matrices) {
return setJointMatrices(Containers::arrayView(matrices));
}
MeshVisualizerGL3D& MeshVisualizerGL3D::setJointMatrix(const UnsignedInt id, const Matrix4& matrix) {
CORRADE_ASSERT(!(flags() >= Flag::UniformBuffers),
"Shaders::MeshVisualizerGL3D::setJointMatrix(): the shader was created with uniform buffers enabled", *this);
CORRADE_ASSERT(id < _jointCount,
"Shaders::MeshVisualizerGL3D::setJointMatrix(): joint ID" << id << "is out of bounds for" << _jointCount << "joints", *this);
setUniform(_jointMatricesUniform + id, matrix);
return *this;
}
#endif
#ifndef MAGNUM_TARGET_GLES2
MeshVisualizerGL3D& MeshVisualizerGL3D::bindProjectionBuffer(GL::Buffer& buffer) {
CORRADE_ASSERT(flags() >= Flag::UniformBuffers,
@ -1205,6 +1436,21 @@ MeshVisualizerGL3D& MeshVisualizerGL3D::bindDrawBuffer(GL::Buffer& buffer, const
}
#endif
#ifndef MAGNUM_TARGET_GLES2
MeshVisualizerGL3D::Configuration& MeshVisualizerGL3D::Configuration::setJointCount(UnsignedInt count, UnsignedInt perVertexCount, UnsignedInt secondaryPerVertexCount) {
CORRADE_ASSERT(perVertexCount <= 4,
"Shaders::MeshVisualizerGL3D::Configuration::setJointCount(): expected at most 4 per-vertex joints, got" << perVertexCount, *this);
CORRADE_ASSERT(secondaryPerVertexCount <= 4,
"Shaders::MeshVisualizerGL3D::Configuration::setJointCount(): expected at most 4 secondary per-vertex joints, got" << secondaryPerVertexCount, *this);
CORRADE_ASSERT(!count == (!perVertexCount && !secondaryPerVertexCount),
"Shaders::MeshVisualizerGL3D::Configuration::setJointCount(): either both joint count and (secondary) per-vertex joint count has to be non-zero, or all zero", *this);
_jointCount = count;
_perVertexJointCount = perVertexCount;
_secondaryPerVertexJointCount = secondaryPerVertexCount;
return *this;
}
#endif
Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flag value) {
#ifndef MAGNUM_TARGET_GLES2
/* Special case coming from the Flags printer. As both flags are a superset
@ -1240,6 +1486,7 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flag value) {
_c(UniformBuffers)
_c(MultiDraw)
_c(TextureArrays)
_c(DynamicPerVertexJointCount)
#endif
#undef _c
/* LCOV_EXCL_STOP */
@ -1289,6 +1536,7 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL3D::Flag value) {
_c(UniformBuffers)
_c(MultiDraw)
_c(TextureArrays)
_c(DynamicPerVertexJointCount)
#endif
#undef _c
/* LCOV_EXCL_STOP */
@ -1325,6 +1573,7 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL2D::Flags value) {
MeshVisualizerGL2D::Flag::MultiDraw, /* Superset of UniformBuffers */
MeshVisualizerGL2D::Flag::UniformBuffers,
MeshVisualizerGL2D::Flag::TextureArrays,
MeshVisualizerGL2D::Flag::DynamicPerVertexJointCount,
#endif
#endif
});
@ -1364,6 +1613,7 @@ Debug& operator<<(Debug& debug, const MeshVisualizerGL3D::Flags value) {
MeshVisualizerGL3D::Flag::MultiDraw, /* Superset of UniformBuffers */
MeshVisualizerGL3D::Flag::UniformBuffers,
MeshVisualizerGL3D::Flag::TextureArrays,
MeshVisualizerGL3D::Flag::DynamicPerVertexJointCount,
#endif
#endif
});

705
src/Magnum/Shaders/MeshVisualizerGL.h

@ -65,6 +65,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr
UniformBuffers = 1 << 10,
MultiDraw = UniformBuffers|(1 << 11),
TextureArrays = 1 << 17,
DynamicPerVertexJointCount = 1 << 18
#endif
};
typedef Containers::EnumSet<FlagBase> FlagsBase;
@ -78,11 +79,12 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr
static MAGNUM_SHADERS_LOCAL void assertExtensions(const FlagsBase flags);
static MAGNUM_SHADERS_LOCAL GL::Version setupShaders(GL::Shader& vert, GL::Shader& frag, const Utility::Resource& rs, const FlagsBase flags
#ifndef MAGNUM_TARGET_GLES2
, UnsignedInt materialCount, UnsignedInt drawCount
, UnsignedInt dimensions, UnsignedInt jointCount, UnsignedInt perVertexJointCount, UnsignedInt secondaryPerVertexJointCount, UnsignedInt materialCount, UnsignedInt drawCount, UnsignedInt perInstanceJointCountUniform, UnsignedInt perVertexJointCountUniform
#endif
);
#ifndef MAGNUM_TARGET_GLES2
MeshVisualizerGLBase& setPerVertexJointCount(UnsignedInt count, UnsignedInt secondaryCount);
MeshVisualizerGLBase& setTextureMatrix(const Matrix3& matrix);
MeshVisualizerGLBase& setTextureLayer(UnsignedInt layer);
MeshVisualizerGLBase& setObjectId(UnsignedInt id);
@ -92,22 +94,29 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr
MeshVisualizerGLBase& setWireframeWidth(Float width);
#ifndef MAGNUM_TARGET_GLES2
MeshVisualizerGLBase& setColorMapTransformation(Float offset, Float scale);
MeshVisualizerGLBase& setPerInstanceJointCount(UnsignedInt count);
MeshVisualizerGLBase& setDrawOffset(UnsignedInt offset);
MeshVisualizerGLBase& bindColorMapTexture(GL::Texture2D& texture);
MeshVisualizerGLBase& bindObjectIdTexture(GL::Texture2D& texture);
MeshVisualizerGLBase& bindObjectIdTexture(GL::Texture2DArray& texture);
#endif
#ifndef MAGNUM_TARGET_GLES2
MeshVisualizerGLBase& setDrawOffset(UnsignedInt offset);
MeshVisualizerGLBase& bindTextureTransformationBuffer(GL::Buffer& buffer);
MeshVisualizerGLBase& bindTextureTransformationBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size);
MeshVisualizerGLBase& bindMaterialBuffer(GL::Buffer& buffer);
MeshVisualizerGLBase& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size);
MeshVisualizerGLBase& bindJointBuffer(GL::Buffer& buffer);
MeshVisualizerGLBase& bindJointBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size);
#endif
FlagsBase _flags;
#ifndef MAGNUM_TARGET_GLES2
UnsignedInt _materialCount{}, _drawCount{};
UnsignedInt _jointCount{},
_perVertexJointCount{},
_secondaryPerVertexJointCount{},
_materialCount{},
_drawCount{};
#endif
Int _viewportSizeUniform{0},
_colorUniform{1},
@ -118,10 +127,15 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGLBase: public GL::AbstractShaderProgr
Int _colorMapOffsetScaleUniform{5},
_objectIdUniform{6},
_textureMatrixUniform{7},
_textureLayerUniform{8};
/* Used instead of all other uniforms except viewportSize when
Flag::UniformBuffers is set, so it can alias them */
Int _drawOffsetUniform{1};
_textureLayerUniform{8},
/* 9 to 13 different between MeshVisualizerGL2D and
MeshVisualizerGL3D */
_jointMatricesUniform{14},
_perInstanceJointCountUniform, /* 14 + jointCount */
/* Used instead of all other uniforms except viewportSize when
Flag::UniformBuffers is set, so it can alias them */
_drawOffsetUniform{1},
_perVertexJointCountUniform; /* 15 + jointCount, or 2 with UBOs */
#endif
};
@ -189,7 +203,8 @@ a trimmed-down @ref MeshVisualizerDrawUniform2D is used instead of
*/
class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisualizerGLBase {
public:
class Configuration;
/* MSVC needs dllexport here as well */
class MAGNUM_SHADERS_EXPORT Configuration;
class CompileState;
/**
@ -205,6 +220,64 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua
typedef GenericGL2D::TextureCoordinates TextureCoordinates;
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Joint ids
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4ui.
* Used only if @ref perVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::JointIds JointIds;
/**
* @brief Weights
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4.
* Used only if @ref perVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::Weights Weights;
/**
* @brief Secondary joint ids
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4ui.
* Used only if @ref secondaryPerVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::SecondaryJointIds SecondaryJointIds;
/**
* @brief Secondary weights
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4.
* Used only if @ref secondaryPerVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::SecondaryWeights SecondaryWeights;
#endif
/**
* @brief Vertex index
*
@ -443,6 +516,29 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua
/** @copydoc MeshVisualizerGL3D::Flag::TextureArrays */
TextureArrays = 1 << 17,
/**
* Dynamic per-vertex joint count for skinning. Uses only the first
* M / N primary / secondary components defined by
* @ref setPerVertexJointCount() instead of
* all primary / secondary components defined by
* @ref Configuration::setJointCount() at shader compilation time.
* Useful in order to avoid having a shader permutation defined for
* every possible joint count. Unfortunately it's not possible to
* make use of default values for unspecified input components as
* the last component is always @cpp 1.0 @ce instead of
* @cpp 0.0 @ce, on the other hand dynamically limiting the joint
* count can reduce the time spent executing the vertex shader
* compared to going through the full set of per-vertex joints
* always.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders,
* which is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
* @m_since_latest
*/
DynamicPerVertexJointCount = 1 << 18,
#endif
};
@ -555,6 +651,47 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua
return Flag(UnsignedInt(Implementation::MeshVisualizerGLBase::_flags));
}
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Joint count
* @m_since_latest
*
* If @ref Flag::UniformBuffers is not set, this is the number of joint
* matrices accepted by @ref setJointMatrices() / @ref setJointMatrix().
* If @ref Flag::UniformBuffers is set, this is the statically defined
* size of the @ref TransformationUniform2D uniform buffer bound with
* @ref bindJointBuffer().
* @see @ref Configuration::setJointCount()
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt jointCount() const { return _jointCount; }
/**
* @brief Per-vertex joint count
* @m_since_latest
*
* Returns the value set with @ref Configuration::setJointCount(). If
* @ref Flag::DynamicPerVertexJointCount is set, the count can be
* additionally modified per-draw using @ref setPerVertexJointCount().
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt perVertexJointCount() const { return _perVertexJointCount; }
/**
* @brief Secondary per-vertex joint count
* @m_since_latest
*
* Returns the value set with @ref Configuration::setJointCount(). If
* @ref Flag::DynamicPerVertexJointCount is set, the count can be
* additionally modified per-draw using @ref setPerVertexJointCount().
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt secondaryPerVertexJointCount() const { return _secondaryPerVertexJointCount; }
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Material count
@ -585,6 +722,37 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua
UnsignedInt drawCount() const { return _drawCount; }
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Set dynamic per-vertex skinning joint count
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Allows reducing the count of iterated joints for a particular draw
* call, making it possible to use a single shader with meshes that
* contain different count of per-vertex joints. See
* @ref Flag::DynamicPerVertexJointCount for more information. As the
* joint count is tied to the mesh layout, this is a per-draw-call
* setting even in case of @ref Flag::UniformBuffers instead of being
* a value in @ref MeshVisualizerDrawUniform2D. Initial value is same
* as @ref perVertexJointCount() and
* @ref secondaryPerVertexJointCount().
*
* Expects that @ref Flag::DynamicPerVertexJointCount is set,
* @p count is not larger than @ref perVertexJointCount() and
* @p secondaryCount not larger than @ref secondaryPerVertexJointCount().
* @see @ref Configuration::setJointCount()
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders, which
* is not available in WebGL 1.0.
*/
MeshVisualizerGL2D& setPerVertexJointCount(UnsignedInt count, UnsignedInt secondaryCount = 0) {
return static_cast<MeshVisualizerGL2D&>(Implementation::MeshVisualizerGLBase::setPerVertexJointCount(count, secondaryCount));
}
#endif
/** @{
* @name Uniform setters
*
@ -722,6 +890,78 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua
*/
MeshVisualizerGL2D& setSmoothness(Float smoothness);
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Set joint matrices
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Initial values are identity transformations. Expects that the size
* of the @p matrices array is the same as @ref jointCount().
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref TransformationUniform2D::transformationMatrix and call
* @ref bindJointBuffer() instead.
* @see @ref setJointMatrix(UnsignedInt, const Matrix3&)
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
MeshVisualizerGL2D& setJointMatrices(const Containers::ArrayView<const Matrix3> matrices);
/**
* @overload
* @m_since_latest
*/
MeshVisualizerGL2D& setJointMatrices(std::initializer_list<Matrix3> matrices);
/**
* @brief Set joint matrix for given joint
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Unlike @ref setJointMatrices() updates just a single joint matrix.
* Expects that @p id is less than @ref jointCount().
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref TransformationUniform2D::transformationMatrix and call
* @ref bindJointBuffer() instead.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
MeshVisualizerGL2D& setJointMatrix(UnsignedInt id, const Matrix3& matrix);
/**
* @brief Set per-instance joint count
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Offset added to joint IDs in the @ref JointIds and
* @ref SecondaryJointIds in instanced draws. Should be less than
* @ref jointCount(). Initial value is @cpp 0 @ce, meaning every
* instance will use the same joint matrices, setting it to a non-zero
* value causes the joint IDs to be interpreted as
* @glsl gl_InstanceID*count + jointId @ce.
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref MeshVisualizerDrawUniform2D::perInstanceJointCount and call
* @ref bindDrawBuffer() instead.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
MeshVisualizerGL2D& setPerInstanceJointCount(UnsignedInt count) {
return static_cast<MeshVisualizerGL2D&>(Implementation::MeshVisualizerGLBase::setPerInstanceJointCount(count));
}
#endif
/**
* @}
*/
@ -834,6 +1074,29 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua
return static_cast<MeshVisualizerGL2D&>(Implementation::MeshVisualizerGLBase::bindMaterialBuffer(buffer, offset, size));
}
/**
* @brief Bind a joint matrix uniform buffer
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Expects that @ref Flag::UniformBuffers is set. The buffer is
* expected to contain @ref jointCount() instances of
* @ref TransformationUniform2D.
* @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object}
* @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0.
* @requires_webgl20 Uniform buffers are not available in WebGL 1.0.
*/
MeshVisualizerGL2D& bindJointBuffer(GL::Buffer& buffer) {
return static_cast<MeshVisualizerGL2D&>(Implementation::MeshVisualizerGLBase::bindJointBuffer(buffer));
}
/**
* @overload
* @m_since_latest
*/
MeshVisualizerGL2D& bindJointBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size) {
return static_cast<MeshVisualizerGL2D&>(Implementation::MeshVisualizerGLBase::bindJointBuffer(buffer, offset, size));
}
/**
* @}
*/
@ -881,7 +1144,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D: public Implementation::MeshVisua
@see @ref MeshVisualizerGL2D(const Configuration&),
@ref compile(const Configuration&)
*/
class MeshVisualizerGL2D::Configuration {
class MAGNUM_SHADERS_EXPORT MeshVisualizerGL2D::Configuration {
public:
explicit Configuration() = default;
@ -903,6 +1166,67 @@ class MeshVisualizerGL2D::Configuration {
}
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Joint count
*
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt jointCount() const { return _jointCount; }
/**
* @brief Per-vertex joint count
*
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt perVertexJointCount() const { return _perVertexJointCount; }
/**
*@brief Secondary per-vertex joint count
*
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt secondaryPerVertexJointCount() const { return _secondaryPerVertexJointCount; }
/**
* @brief Set joint count
*
* If @ref Flag::UniformBuffers isn't set, @p count describes how many
* joint matrices get supplied to each draw by @ref setJointMatrices()
* / @ref setJointMatrix(). If @ref Flag::UniformBuffers is set,
* @p count describes size of a @ref TransformationUniform2D buffer
* bound with @ref bindJointBuffer(); as uniform buffers are required
* to have a statically defined size. The per-vertex joints then index
* into the array offset by
* @ref MeshVisualizerDrawUniform2D::jointOffset. If @p count is
* @cpp 0 @ce, skinning is not performed.
*
* The @p perVertexCount and @p secondaryPerVertexCount then describe
* how many components are taken from @ref JointIds / @ref Weights and
* @ref SecondaryJointIds / @ref SecondaryWeights attributes. Both
* values are expected to not be larger than @cpp 4 @ce, setting either
* of these to @cpp 0 @ce means given attribute is not used at all. If
* @p count is @cpp 0 @ce, both @p perVertexCount and
* @p secondaryPerVertexCount is expected to be @cpp 0 @ce as well; if
* @p count is non-zero at least one of @p perVertexCount and
* @p secondaryPerVertexCount is expected to be non-zero as well.
*
* Default value for all three is @cpp 0 @ce.
* @see @ref MeshVisualizerGL2D::jointCount(),
* @ref MeshVisualizerGL2D::perVertexJointCount(),
* @ref MeshVisualizerGL2D::secondaryPerVertexJointCount(),
* @ref Flag::DynamicPerVertexJointCount,
* @ref MeshVisualizerGL2D::setPerVertexJointCount()
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
Configuration& setJointCount(UnsignedInt count, UnsignedInt perVertexCount, UnsignedInt secondaryPerVertexCount = 0);
/**
* @brief Material count
*
@ -969,7 +1293,10 @@ class MeshVisualizerGL2D::Configuration {
private:
Flags _flags;
#ifndef MAGNUM_TARGET_GLES2
UnsignedInt _materialCount = 1,
UnsignedInt _jointCount = 0,
_perVertexJointCount = 0,
_secondaryPerVertexJointCount = 0,
_materialCount = 1,
_drawCount = 1;
#endif
};
@ -1171,6 +1498,28 @@ non-indexed @ref MeshPrimitive::Triangles.
arrays are not available in WebGL 1.0.
@requires_webgl20 `gl_VertexID` is not available in WebGL 1.0.
@section Shaders-MeshVisualizerGL3D-skinning Skinning
To render skinned meshes, bind up to two sets of up to four-component joint ID
and weight attributes to @ref JointIds / @ref SecondaryJointIds and
@ref Weights / @ref SecondaryWeights, set an appropriate joint count and
per-vertex primary and secondary joint count in
@ref Configuration::setJointCount() and upload appropriate joint matrices with
@ref setJointMatrices(). Currently, the mesh visualizer supports only
transforming the mesh vertices for feature parity with other shaders,
no skinning-specific visualization feature is implemented.
To avoid having to compile multiple shader variants for different per-vertex
joint counts, enable @ref Flag::DynamicPerVertexJointCount, set the maximum
per-vertex joint count in @ref Configuration::setJointCount() and then adjust
the actual per-draw joint count with @ref setPerVertexJointCount().
@requires_gl30 Extension @gl_extension{EXT,texture_integer}
@requires_gles30 Skinning requires integer support in shaders, which is not
available in OpenGL ES 2.0.
@requires_webgl20 Skinning requires integer support in shaders, which is not
available in WebGL 1.0.
@section Shaders-MeshVisualizerGL3D-instancing Instanced rendering
Enabling @ref Flag::InstancedTransformation will turn the shader into an
@ -1190,6 +1539,12 @@ enabled, the @ref TextureOffset attribute (or @ref TextureOffsetLayer in case
@ref Flag::TextureArrays is enabled as well) then can supply per-instance
texture offset (or offset and layer).
For instanced skinning the joint buffer is assumed to contain joint
transformations for all instances. By default all instances use the same joint
transformations, seting @ref setPerInstanceJointCount() will cause the shader
to offset the per-vertex joint IDs with
@glsl gl_InstanceID*perInstanceJointCount @ce.
@requires_gl33 Extension @gl_extension{ARB,instanced_arrays}
@requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays},
@gl_extension{EXT,instanced_arrays} or @gl_extension{NV,instanced_arrays}
@ -1223,6 +1578,18 @@ and draw count via @ref Configuration::setMaterialCount() and
every draw. The usage is similar for all shaders, see
@ref shaders-usage-multidraw for an example.
For skinning, joint matrices are supplied via a @ref TransformationUniform3D
buffer bound with @ref bindJointBuffer(). In an instanced scenario the
per-instance joint count is supplied via
@ref MeshVisualizerDrawUniform3D::perInstanceJointCount, a per-draw joint
offset for the multidraw scenario is supplied via
@ref MeshVisualizerDrawUniform3D::jointOffset. Altogether for a particular
draw, each per-vertex joint ID is offset with
@glsl gl_InstanceID*perInstanceJointCount + jointOffset @ce. The
@ref setPerVertexJointCount() stays as an immediate uniform in the UBO and
multidraw scenario as well, as it is tied to a particular mesh layout and thus
doesn't need to vary per draw.
@requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} for uniform
buffers.
@requires_gl46 Extension @gl_extension{ARB,shader_draw_parameters} for
@ -1239,7 +1606,8 @@ every draw. The usage is similar for all shaders, see
*/
class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisualizerGLBase {
public:
class Configuration;
/* MSVC needs dllexport here as well */
class MAGNUM_SHADERS_EXPORT Configuration;
class CompileState;
/**
@ -1308,6 +1676,64 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua
typedef GenericGL3D::TextureCoordinates TextureCoordinates;
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Joint ids
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4ui.
* Used only if @ref perVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::JointIds JointIds;
/**
* @brief Weights
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4.
* Used only if @ref perVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::Weights Weights;
/**
* @brief Secondary joint ids
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4ui.
* Used only if @ref secondaryPerVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::SecondaryJointIds SecondaryJointIds;
/**
* @brief Secondary weights
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4.
* Used only if @ref secondaryPerVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::SecondaryWeights SecondaryWeights;
#endif
/**
* @brief Vertex index
*
@ -1749,7 +2175,30 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua
* @m_since_latest
* @todoc rewrite the ext requirements once we have more textures
*/
TextureArrays = 1 << 17
TextureArrays = 1 << 17,
/**
* Dynamic per-vertex joint count for skinning. Uses only the first
* M / N primary / secondary components defined by
* @ref setPerVertexJointCount() instead of
* all primary / secondary components defined by
* @ref Configuration::setJointCount() at shader compilation time.
* Useful in order to avoid having a shader permutation defined for
* every possible joint count. Unfortunately it's not possible to
* make use of default values for unspecified input components as
* the last component is always @cpp 1.0 @ce instead of
* @cpp 0.0 @ce, on the other hand dynamically limiting the joint
* count can reduce the time spent executing the vertex shader
* compared to going through the full set of per-vertex joints
* always.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders,
* which is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
* @m_since_latest
*/
DynamicPerVertexJointCount = 1 << 18,
#endif
};
@ -1870,6 +2319,47 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua
return Flag(UnsignedInt(Implementation::MeshVisualizerGLBase::_flags));
}
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Joint count
* @m_since_latest
*
* If @ref Flag::UniformBuffers is not set, this is the number of joint
* matrices accepted by @ref setJointMatrices() / @ref setJointMatrix().
* If @ref Flag::UniformBuffers is set, this is the statically defined
* size of the @ref TransformationUniform3D uniform buffer bound with
* @ref bindJointBuffer().
* @see @ref Configuration::setJointCount()
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt jointCount() const { return _jointCount; }
/**
* @brief Per-vertex joint count
* @m_since_latest
*
* Returns the value set with @ref Configuration::setJointCount(). If
* @ref Flag::DynamicPerVertexJointCount is set, the count can be
* additionally modified per-draw using @ref setPerVertexJointCount().
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt perVertexJointCount() const { return _perVertexJointCount; }
/**
* @brief Secondary per-vertex joint count
* @m_since_latest
*
* Returns the value set with @ref Configuration::setJointCount(). If
* @ref Flag::DynamicPerVertexJointCount is set, the count can be
* additionally modified per-draw using @ref setPerVertexJointCount().
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt secondaryPerVertexJointCount() const { return _secondaryPerVertexJointCount; }
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Material count
@ -1900,6 +2390,37 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua
UnsignedInt drawCount() const { return _drawCount; }
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Set dynamic per-vertex skinning joint count
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Allows reducing the count of iterated joints for a particular draw
* call, making it possible to use a single shader with meshes that
* contain different count of per-vertex joints. See
* @ref Flag::DynamicPerVertexJointCount for more information. As the
* joint count is tied to the mesh layout, this is a per-draw-call
* setting even in case of @ref Flag::UniformBuffers instead of being
* a value in @ref MeshVisualizerDrawUniform3D. Initial value is same
* as @ref perVertexJointCount() and
* @ref secondaryPerVertexJointCount().
*
* Expects that @ref Flag::DynamicPerVertexJointCount is set,
* @p count is not larger than @ref perVertexJointCount() and
* @p secondaryCount not larger than @ref secondaryPerVertexJointCount().
* @see @ref Configuration::setJointCount()
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders, which
* is not available in WebGL 1.0.
*/
MeshVisualizerGL3D& setPerVertexJointCount(UnsignedInt count, UnsignedInt secondaryCount = 0) {
return static_cast<MeshVisualizerGL3D&>(Implementation::MeshVisualizerGLBase::setPerVertexJointCount(count, secondaryCount));
}
#endif
/** @{
* @name Uniform setters
*
@ -2216,6 +2737,78 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua
*/
MeshVisualizerGL3D& setSmoothness(Float smoothness);
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Set joint matrices
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Initial values are identity transformations. Expects that the size
* of the @p matrices array is the same as @ref jointCount().
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref TransformationUniform3D::transformationMatrix and call
* @ref bindJointBuffer() instead.
* @see @ref setJointMatrix(UnsignedInt, const Matrix4&)
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
MeshVisualizerGL3D& setJointMatrices(const Containers::ArrayView<const Matrix4> matrices);
/**
* @overload
* @m_since_latest
*/
MeshVisualizerGL3D& setJointMatrices(std::initializer_list<Matrix4> matrices);
/**
* @brief Set joint matrix for given joint
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Unlike @ref setJointMatrices() updates just a single joint matrix.
* Expects that @p id is less than @ref jointCount().
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref TransformationUniform3D::transformationMatrix and call
* @ref bindJointBuffer() instead.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
MeshVisualizerGL3D& setJointMatrix(UnsignedInt id, const Matrix4& matrix);
/**
* @brief Set per-instance joint count
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Offset added to joint IDs in the @ref JointIds and
* @ref SecondaryJointIds in instanced draws. Should be less than
* @ref jointCount(). Initial value is @cpp 0 @ce, meaning every
* instance will use the same joint matrices, setting it to a non-zero
* value causes the joint IDs to be interpreted as
* @glsl gl_InstanceID*count + jointId @ce.
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref MeshVisualizerDrawUniform3D::perInstanceJointCount and call
* @ref bindDrawBuffer() instead.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
MeshVisualizerGL3D& setPerInstanceJointCount(UnsignedInt count) {
return static_cast<MeshVisualizerGL3D&>(Implementation::MeshVisualizerGLBase::setPerInstanceJointCount(count));
}
#endif
/**
* @}
*/
@ -2367,6 +2960,29 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerGL3D: public Implementation::MeshVisua
return static_cast<MeshVisualizerGL3D&>(Implementation::MeshVisualizerGLBase::bindMaterialBuffer(buffer, offset, size));
}
/**
* @brief Bind a joint matrix uniform buffer
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Expects that @ref Flag::UniformBuffers is set. The buffer is
* expected to contain @ref jointCount() instances of
* @ref TransformationUniform3D.
* @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object}
* @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0.
* @requires_webgl20 Uniform buffers are not available in WebGL 1.0.
*/
MeshVisualizerGL3D& bindJointBuffer(GL::Buffer& buffer) {
return static_cast<MeshVisualizerGL3D&>(Implementation::MeshVisualizerGLBase::bindJointBuffer(buffer));
}
/**
* @overload
* @m_since_latest
*/
MeshVisualizerGL3D& bindJointBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size) {
return static_cast<MeshVisualizerGL3D&>(Implementation::MeshVisualizerGLBase::bindJointBuffer(buffer, offset, size));
}
/**
* @}
*/
@ -2497,6 +3113,66 @@ class MeshVisualizerGL3D::Configuration {
}
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Joint count
*
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt jointCount() const { return _jointCount; }
/**
* @brief Per-vertex joint count
*
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt perVertexJointCount() const { return _perVertexJointCount; }
/**
*@brief Secondary per-vertex joint count
*
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt secondaryPerVertexJointCount() const { return _secondaryPerVertexJointCount; }
/**
* @brief Set joint count
*
* If @ref Flag::UniformBuffers isn't set, @p count describes how many
* joint matrices get supplied to each draw by @ref setJointMatrices()
* / @ref setJointMatrix(). If @ref Flag::UniformBuffers is set,
* @p count describes size of a @ref TransformationUniform3D buffer
* bound with @ref bindJointBuffer(); as uniform buffers are required
* to have a statically defined size. The per-vertex joints then index
* into the array offset by @ref MeshVisualizerDrawUniform3D::jointOffset.
* If @p count is @cpp 0 @ce, skinning is not performed.
*
* The @p perVertexCount and @p secondaryPerVertexCount then describe
* how many components are taken from @ref JointIds / @ref Weights and
* @ref SecondaryJointIds / @ref SecondaryWeights attributes. Both
* values are expected to not be larger than @cpp 4 @ce, setting either
* of these to @cpp 0 @ce means given attribute is not used at all. If
* @p count is @cpp 0 @ce, both @p perVertexCount and
* @p secondaryPerVertexCount is expected to be @cpp 0 @ce as well; if
* @p count is non-zero at least one of @p perVertexCount and
* @p secondaryPerVertexCount is expected to be non-zero as well.
*
* Default value for all three is @cpp 0 @ce.
* @see @ref MeshVisualizerGL2D::jointCount(),
* @ref MeshVisualizerGL2D::perVertexJointCount(),
* @ref MeshVisualizerGL2D::secondaryPerVertexJointCount(),
* @ref Flag::DynamicPerVertexJointCount,
* @ref MeshVisualizerGL2D::setPerVertexJointCount()
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
Configuration& setJointCount(UnsignedInt count, UnsignedInt perVertexCount, UnsignedInt secondaryPerVertexCount = 0);
/**
* @brief Material count
*
@ -2562,7 +3238,10 @@ class MeshVisualizerGL3D::Configuration {
private:
Flags _flags;
#ifndef MAGNUM_TARGET_GLES2
UnsignedInt _materialCount = 1,
UnsignedInt _jointCount = 0,
_perVertexJointCount = 0,
_secondaryPerVertexJointCount = 0,
_materialCount = 1,
_drawCount = 1;
#endif
};

9
src/Magnum/Shaders/Phong.frag

@ -183,10 +183,11 @@ uniform highp uint drawOffset
struct DrawUniform {
/* Can't be a mat3 because of ANGLE, see Phong.vert for details */
mediump mat3x4 normalMatrix;
highp uvec4 materialIdReservedObjectIdLightOffsetLightCountReserved;
#define draw_materialIdReserved materialIdReservedObjectIdLightOffsetLightCountReserved.x
#define draw_objectId materialIdReservedObjectIdLightOffsetLightCountReserved.y
#define draw_lightOffsetLightCount materialIdReservedObjectIdLightOffsetLightCountReserved.z
highp uvec4 materialIdReservedObjectIdLightOffsetLightCountJointOffsetPerInstanceJointCount;
#define draw_materialIdReserved materialIdReservedObjectIdLightOffsetLightCountJointOffsetPerInstanceJointCount.x
#define draw_objectId materialIdReservedObjectIdLightOffsetLightCountJointOffsetPerInstanceJointCount.y
#define draw_lightOffsetLightCount materialIdReservedObjectIdLightOffsetLightCountJointOffsetPerInstanceJointCount.z
#define draw_jointOffsetPerInstanceJointCount materialIdReservedObjectIdLightOffsetLightCountJointOffsetPerInstanceJointCount.w
};
layout(std140

61
src/Magnum/Shaders/Phong.h

@ -57,9 +57,9 @@ struct PhongDrawUniform {
/** @brief Construct with default parameters */
constexpr explicit PhongDrawUniform(DefaultInitT = DefaultInit) noexcept: normalMatrix{Math::IdentityInit}, materialId{0}, objectId{0},
#ifndef CORRADE_TARGET_BIG_ENDIAN
lightOffset{0}, lightCount{0xffffu}
lightOffset{0}, lightCount{0xffffu}, jointOffset{0}, perInstanceJointCount{0}
#else
lightCount{0xffffu}, lightOffset{0}
lightCount{0xffffu}, lightOffset{0}, perInstanceJointCount{0}, jointOffset{0}
#endif
{}
@ -115,6 +115,24 @@ struct PhongDrawUniform {
return *this;
}
/**
* @brief Set the @ref jointOffset field
* @return Reference to self (for method chaining)
*/
PhongDrawUniform& setJointOffset(UnsignedInt offset) {
jointOffset = offset;
return *this;
}
/**
* @brief Set the @ref perInstanceJointCount field
* @return Reference to self (for method chaining)
*/
PhongDrawUniform& setPerInstanceJointCount(UnsignedInt count) {
perInstanceJointCount = count;
return *this;
}
/**
* @}
*/
@ -151,10 +169,10 @@ struct PhongDrawUniform {
/* warning: Member __pad0__ is not documented. FFS DOXYGEN WHY DO YOU THINK
I MADE THOSE UNNAMED, YOU DUMB FOOL */
#ifndef DOXYGEN_GENERATING_OUTPUT
UnsignedShort:16; /* reserved for skinOffset */
UnsignedShort:16; /* reserved */
#endif
#else
UnsignedShort:16; /* reserved for skinOffset */
UnsignedShort:16; /* reserved */
UnsignedShort materialId;
#endif
@ -207,10 +225,37 @@ struct PhongDrawUniform {
UnsignedShort lightOffset;
#endif
/* warning: Member __pad1__ is not documented. FFS DOXYGEN WHY DO YOU THINK
I MADE THOSE UNNAMED, YOU DUMB FOOL */
#ifndef DOXYGEN_GENERATING_OUTPUT
Int:32;
/** @var jointOffset
* @brief Joint offset
*
* Offset added to joint IDs in the @ref PhongGL::JointIds and
* @ref PhongGL::SecondaryJointIds attributes. Useful when a UBO with joint
* matrices for more than one skin is supplied or in a multi-draw scenario.
* Should be less than the joint count passed to
* @ref PhongGL::Configuration::setJointCount(). Default value is
* @cpp 0 @ce, meaning no offset is added to joint IDs.
*/
/** @var perInstanceJointCount
* @brief Per-instance joint count
*
* Offset added to joint IDs in the @ref PhongGL::JointIds and
* @ref PhongGL::SecondaryJointIds atttributes in instanced draws. Should
* be less than the joint count passed to
* @ref PhongGL::Configuration::setJointCount(). Default value is
* @cpp 0 @ce, meaning every instance will use the same joint matrices,
* setting it to a non-zero value causes the joint IDs to be interpreted as
* @glsl gl_InstanceID*count + jointId @ce.
*/
/* This field is an UnsignedInt in the shader and jointOffset is extracted
as (value & 0xffff), so the order has to be different on BE */
#ifndef CORRADE_TARGET_BIG_ENDIAN
UnsignedShort jointOffset;
UnsignedShort perInstanceJointCount;
#else
UnsignedShort perInstanceJointCount;
UnsignedShort jointOffset;
#endif
};

104
src/Magnum/Shaders/Phong.vert

@ -48,6 +48,19 @@
#define const
#endif
/* Both classic uniforms and uniform buffers */
#ifdef DYNAMIC_PER_VERTEX_JOINT_COUNT
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = PER_VERTEX_JOINT_COUNT_LOCATION)
#endif
uniform mediump uvec2 perVertexJointCount
#ifndef GL_ES
= uvec2(PER_VERTEX_JOINT_COUNT, SECONDARY_PER_VERTEX_JOINT_COUNT)
#endif
;
#endif
/* Uniforms */
#ifndef UNIFORM_BUFFERS
@ -99,6 +112,22 @@ layout(location = 4)
uniform highp uint textureLayer; /* defaults to zero */
#endif
#ifdef JOINT_COUNT
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = JOINT_MATRICES_LOCATION)
#endif
uniform mat4 jointMatrices[JOINT_COUNT]
#ifndef GL_ES
= mat4[](JOINT_MATRIX_INITIALIZER)
#endif
;
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = PER_INSTANCE_JOINT_COUNT_LOCATION)
#endif
uniform uint perInstanceJointCount; /* defaults to zero */
#endif
/* Uniform buffers */
#else
@ -132,10 +161,11 @@ struct DrawUniform {
2. Forget to actually implement and test the damn thing.
*/
mediump mat3x4 normalMatrix;
highp uvec4 materialIdReservedObjectIdLightOffsetLightCountReserved;
#define draw_materialIdReserved materialIdReservedObjectIdLightOffsetLightCountReserved.x
#define draw_objectId materialIdReservedObjectIdLightOffsetLightCountReserved.y
#define draw_lightOffsetLightCount materialIdReservedObjectIdLightOffsetLightCountReserved.z
highp uvec4 materialIdReservedObjectIdLightOffsetLightCountJointOffsetPerInstanceJointCount;
#define draw_materialIdReserved materialIdReservedObjectIdLightOffsetLightCountJointOffsetPerInstanceJointCount.x
#define draw_objectId materialIdReservedObjectIdLightOffsetLightCountJointOffsetPerInstanceJointCount.y
#define draw_lightOffsetLightCount materialIdReservedObjectIdLightOffsetLightCountJointOffsetPerInstanceJointCount.z
#define draw_jointOffsetPerInstanceJointCount materialIdReservedObjectIdLightOffsetLightCountJointOffsetPerInstanceJointCount.w
};
layout(std140
@ -162,6 +192,16 @@ layout(std140
highp mat4 transformationMatrices[DRAW_COUNT];
};
#ifdef JOINT_COUNT
layout(std140
#ifdef EXPLICIT_BINDING
, binding = 6
#endif
) uniform Joint {
highp mat4 jointMatrices[JOINT_COUNT];
};
#endif
#ifdef TEXTURE_TRANSFORMATION
struct TextureTransformationUniform {
highp vec4 rotationScaling;
@ -228,6 +268,32 @@ layout(location = COLOR_ATTRIBUTE_LOCATION)
in lowp vec4 vertexColor;
#endif
#ifdef JOINT_COUNT
#if PER_VERTEX_JOINT_COUNT
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = WEIGHTS_ATTRIBUTE_LOCATION)
#endif
in mediump vec4 weights;
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = JOINTIDS_ATTRIBUTE_LOCATION)
#endif
in mediump uvec4 jointIds;
#endif
#if SECONDARY_PER_VERTEX_JOINT_COUNT
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = SECONDARY_WEIGHTS_ATTRIBUTE_LOCATION)
#endif
in mediump vec4 secondaryWeights;
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = SECONDARY_JOINTIDS_ATTRIBUTE_LOCATION)
#endif
in mediump uvec4 secondaryJointIds;
#endif
#endif
#ifdef INSTANCED_OBJECT_ID
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = OBJECT_ID_ATTRIBUTE_LOCATION)
@ -323,6 +389,33 @@ void main() {
highp const uint textureLayer = floatBitsToUint(textureTransformations[drawId].textureTransformation_layer);
#endif
#endif
#ifdef JOINT_COUNT
mediump const uint jointOffset = (draws[drawId].draw_jointOffsetPerInstanceJointCount & 0xffffu) + uint(gl_InstanceID)*(draws[drawId].draw_jointOffsetPerInstanceJointCount >> 16 & 0xffffu);
#endif
#else
#ifdef JOINT_COUNT
mediump const uint jointOffset = uint(gl_InstanceID)*perInstanceJointCount;
#endif
#endif
#ifdef JOINT_COUNT
mat4 skinMatrix = mat4(0.0);
#if PER_VERTEX_JOINT_COUNT
for(uint i = 0u; i != PER_VERTEX_JOINT_COUNT
#ifdef DYNAMIC_PER_VERTEX_JOINT_COUNT
&& i != perVertexJointCount.x
#endif
; ++i)
skinMatrix += weights[i]*jointMatrices[jointOffset + jointIds[i]];
#endif
#if SECONDARY_PER_VERTEX_JOINT_COUNT
for(uint i = 0u; i != SECONDARY_PER_VERTEX_JOINT_COUNT
#ifdef DYNAMIC_PER_VERTEX_JOINT_COUNT
&& i != perVertexJointCount.y
#endif
; ++i)
skinMatrix += secondaryWeights[i]*jointMatrices[jointOffset + secondaryJointIds[i]];
#endif
#endif
/* Transformed vertex position */
@ -330,6 +423,9 @@ void main() {
#ifdef INSTANCED_TRANSFORMATION
instancedTransformationMatrix*
#endif
#ifdef JOINT_COUNT
skinMatrix*
#endif
position;
#ifndef HAS_LIGHTS
highp vec3

171
src/Magnum/Shaders/PhongGL.cpp

@ -53,6 +53,8 @@
namespace Magnum { namespace Shaders {
using namespace Containers::Literals;
namespace {
enum: Int {
AmbientTextureUnit = 0,
@ -65,12 +67,16 @@ namespace {
#ifndef MAGNUM_TARGET_GLES2
enum: Int {
/* Projection, transformation, texture transformation and joints is
slots 0, 1, 3, 6 in all shaders so shaders can be switched without
rebinding everything */
ProjectionBufferBinding = 0,
TransformationBufferBinding = 1,
DrawBufferBinding = 2,
TextureTransformationBufferBinding = 3,
MaterialBufferBinding = 4,
LightBufferBinding = 5
LightBufferBinding = 5,
JointBufferBinding = 6,
};
#endif
}
@ -112,6 +118,13 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) {
CORRADE_ASSERT(!(configuration.flags() & Flag::SpecularTexture) || !(configuration.flags() & (Flag::NoSpecular)),
"Shaders::PhongGL: specular texture requires the shader to not have specular disabled", CompileState{NoCreate});
#ifndef MAGNUM_TARGET_GLES2
CORRADE_ASSERT(!(configuration.flags() & Flag::DynamicPerVertexJointCount) || configuration.jointCount(),
"Shaders::PhongGL: dynamic per-vertex joint count enabled for zero joints", CompileState{NoCreate});
CORRADE_ASSERT(!(configuration.flags() & Flag::InstancedTransformation) || !configuration.secondaryPerVertexJointCount(),
"Shaders::PhongGL: TransformationMatrix attribute binding conflicts with the SecondaryJointIds / SecondaryWeights attributes, use a non-instanced rendering with secondary weights instead", CompileState{NoCreate});
#endif
#ifndef MAGNUM_TARGET_GLES
if(configuration.flags() >= Flag::UniformBuffers)
MAGNUM_ASSERT_GL_EXTENSION_SUPPORTED(GL::Extensions::ARB::uniform_buffer_object);
@ -155,13 +168,22 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) {
PhongGL out{NoInit};
out._flags = configuration.flags();
out._lightCount = configuration.lightCount();
out._lightColorsUniform = out._lightPositionsUniform + Int(configuration.lightCount());
out._lightSpecularColorsUniform = out._lightPositionsUniform + 2*Int(configuration.lightCount());
out._lightRangesUniform = out._lightPositionsUniform + 3*Int(configuration.lightCount());
#ifndef MAGNUM_TARGET_GLES2
out._jointCount = configuration.jointCount();
out._perVertexJointCount = configuration.perVertexJointCount();
out._secondaryPerVertexJointCount = configuration.secondaryPerVertexJointCount();
out._materialCount = configuration.materialCount();
out._drawCount = configuration.drawCount();
#endif
out._lightColorsUniform = out._lightPositionsUniform + Int(configuration.lightCount());
out._lightSpecularColorsUniform = out._lightColorsUniform + Int(configuration.lightCount());
out._lightRangesUniform = out._lightSpecularColorsUniform + Int(configuration.lightCount());
#ifndef MAGNUM_TARGET_GLES2
out._jointMatricesUniform = out._lightRangesUniform + Int(configuration.lightCount());
out._perInstanceJointCountUniform = out._jointMatricesUniform + configuration.jointCount();
out._perVertexJointCountUniform = configuration.flags() >= Flag::UniformBuffers ?
1 : out._perInstanceJointCountUniform + 1;
#endif
GL::Shader vert = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Vertex);
GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment);
@ -234,6 +256,33 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) {
.addSource(configuration.flags() & Flag::InstancedTransformation ? "#define INSTANCED_TRANSFORMATION\n" : "")
.addSource(configuration.flags() >= Flag::InstancedTextureOffset ? "#define INSTANCED_TEXTURE_OFFSET\n" : "");
#ifndef MAGNUM_TARGET_GLES2
if(configuration.jointCount()) {
vert.addSource(Utility::formatString(
"#define JOINT_COUNT {}\n"
"#define PER_VERTEX_JOINT_COUNT {}u\n"
"#define SECONDARY_PER_VERTEX_JOINT_COUNT {}u\n"
"#define JOINT_MATRICES_LOCATION {}\n"
#ifndef MAGNUM_TARGET_GLES
"#define JOINT_MATRIX_INITIALIZER {}\n"
#endif
"#define PER_INSTANCE_JOINT_COUNT_LOCATION {}\n",
configuration.jointCount(),
configuration.perVertexJointCount(),
configuration.secondaryPerVertexJointCount(),
out._jointMatricesUniform,
#ifndef MAGNUM_TARGET_GLES
("mat4(1.0), "_s*configuration.jointCount()).exceptSuffix(2),
#endif
out._perInstanceJointCountUniform));
}
if(configuration.flags() >= Flag::DynamicPerVertexJointCount) {
vert.addSource(Utility::formatString(
"#define DYNAMIC_PER_VERTEX_JOINT_COUNT\n"
"#define PER_VERTEX_JOINT_COUNT_LOCATION {}\n",
out._perVertexJointCountUniform));
}
#endif
#ifndef MAGNUM_TARGET_GLES2
if(configuration.flags() >= Flag::UniformBuffers) {
vert.addSource(Utility::formatString(
"#define UNIFORM_BUFFERS\n"
@ -337,6 +386,19 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) {
}
if(configuration.flags() >= Flag::InstancedTextureOffset)
out.bindAttributeLocation(TextureOffset::Location, "instancedTextureOffset");
#ifndef MAGNUM_TARGET_GLES2
/* Configuration::setJointCount() checks that jointCount and
perVertexJointCount / secondaryPerVertexJointCount are either all
zero or non-zero so we don't need to check for jointCount() here */
if(configuration.perVertexJointCount()) {
out.bindAttributeLocation(Weights::Location, "weights");
out.bindAttributeLocation(JointIds::Location, "jointIds");
}
if(configuration.secondaryPerVertexJointCount()) {
out.bindAttributeLocation(SecondaryWeights::Location, "secondaryWeights");
out.bindAttributeLocation(SecondaryJointIds::Location, "secondaryJointIds");
}
#endif
}
#endif
@ -382,6 +444,8 @@ PhongGL::PhongGL(CompileState&& state): PhongGL{static_cast<PhongGL&&>(std::move
#endif
{
#ifndef MAGNUM_TARGET_GLES2
if(_flags >= Flag::DynamicPerVertexJointCount)
_perVertexJointCountUniform = uniformLocation("perVertexJointCount");
if(_flags >= Flag::UniformBuffers) {
if(_drawCount > 1) _drawOffsetUniform = uniformLocation("drawOffset");
} else
@ -415,6 +479,12 @@ PhongGL::PhongGL(CompileState&& state): PhongGL{static_cast<PhongGL&&>(std::move
#ifndef MAGNUM_TARGET_GLES2
if(_flags & Flag::ObjectId) _objectIdUniform = uniformLocation("objectId");
#endif
#ifndef MAGNUM_TARGET_GLES2
if(_jointCount) {
_jointMatricesUniform = uniformLocation("jointMatrices");
_perInstanceJointCountUniform = uniformLocation("perInstanceJointCount");
}
#endif
}
}
@ -439,6 +509,8 @@ PhongGL::PhongGL(CompileState&& state): PhongGL{static_cast<PhongGL&&>(std::move
setUniformBlockBinding(uniformBlockIndex("TextureTransformation"), TextureTransformationBufferBinding);
if(_lightCount)
setUniformBlockBinding(uniformBlockIndex("Light"), LightBufferBinding);
if(_jointCount)
setUniformBlockBinding(uniformBlockIndex("Joint"), JointBufferBinding);
}
#endif
}
@ -446,6 +518,10 @@ PhongGL::PhongGL(CompileState&& state): PhongGL{static_cast<PhongGL&&>(std::move
/* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */
#ifdef MAGNUM_TARGET_GLES
#ifndef MAGNUM_TARGET_GLES2
if(_flags >= Flag::DynamicPerVertexJointCount)
setPerVertexJointCount(_perVertexJointCount, _secondaryPerVertexJointCount);
#endif
#ifndef MAGNUM_TARGET_GLES2
if(_flags >= Flag::UniformBuffers) {
/* Draw offset is zero by default */
} else
@ -478,6 +554,12 @@ PhongGL::PhongGL(CompileState&& state): PhongGL{static_cast<PhongGL&&>(std::move
/* Texture layer is zero by default */
if(_flags & Flag::AlphaMask) setAlphaMask(0.5f);
/* Object ID is zero by default */
#ifndef MAGNUM_TARGET_GLES2
if(_jointCount) {
setJointMatrices(Containers::Array<Matrix4>{DirectInit, _jointCount, Math::IdentityInit});
/* Per-instance joint count is zero by default */
}
#endif
}
#endif
}
@ -500,6 +582,19 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount, const Unsigned
#endif
#endif
#ifndef MAGNUM_TARGET_GLES2
PhongGL& PhongGL::setPerVertexJointCount(const UnsignedInt count, const UnsignedInt secondaryCount) {
CORRADE_ASSERT(_flags >= Flag::DynamicPerVertexJointCount,
"Shaders::PhongGL::setPerVertexJointCount(): the shader was not created with dynamic per-vertex joint count enabled", *this);
CORRADE_ASSERT(count <= _perVertexJointCount,
"Shaders::PhongGL::setPerVertexJointCount(): expected at most" << _perVertexJointCount << "per-vertex joints, got" << count, *this);
CORRADE_ASSERT(secondaryCount <= _secondaryPerVertexJointCount,
"Shaders::PhongGL::setPerVertexJointCount(): expected at most" << _secondaryPerVertexJointCount << "secondary per-vertex joints, got" << secondaryCount, *this);
setUniform(_perVertexJointCountUniform, Vector2ui{count, secondaryCount});
return *this;
}
#endif
PhongGL& PhongGL::setAmbientColor(const Magnum::Color4& color) {
#ifndef MAGNUM_TARGET_GLES2
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
@ -786,6 +881,37 @@ PhongGL& PhongGL::setLightRange(const UnsignedInt id, const Float range) {
return *this;
}
#ifndef MAGNUM_TARGET_GLES2
PhongGL& PhongGL::setJointMatrices(const Containers::ArrayView<const Matrix4> matrices) {
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
"Shaders::PhongGL::setJointMatrices(): the shader was created with uniform buffers enabled", *this);
CORRADE_ASSERT(_jointCount == matrices.size(),
"Shaders::PhongGL::setJointMatrices(): expected" << _jointCount << "items but got" << matrices.size(), *this);
if(_jointCount) setUniform(_jointMatricesUniform, matrices);
return *this;
}
PhongGL& PhongGL::setJointMatrices(const std::initializer_list<Matrix4> matrices) {
return setJointMatrices(Containers::arrayView(matrices));
}
PhongGL& PhongGL::setJointMatrix(const UnsignedInt id, const Matrix4& matrix) {
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
"Shaders::PhongGL::setJointMatrix(): the shader was created with uniform buffers enabled", *this);
CORRADE_ASSERT(id < _jointCount,
"Shaders::PhongGL::setJointMatrix(): joint ID" << id << "is out of bounds for" << _jointCount << "joints", *this);
setUniform(_jointMatricesUniform + id, matrix);
return *this;
}
PhongGL& PhongGL::setPerInstanceJointCount(const UnsignedInt count) {
CORRADE_ASSERT(!(_flags >= Flag::UniformBuffers),
"Shaders::PhongGL::setPerInstanceJointCount(): the shader was created with uniform buffers enabled", *this);
setUniform(_perInstanceJointCountUniform, count);
return *this;
}
#endif
#ifndef MAGNUM_TARGET_GLES2
PhongGL& PhongGL::setDrawOffset(const UnsignedInt offset) {
CORRADE_ASSERT(_flags >= Flag::UniformBuffers,
@ -883,6 +1009,20 @@ PhongGL& PhongGL::bindLightBuffer(GL::Buffer& buffer, const GLintptr offset, con
buffer.bind(GL::Buffer::Target::Uniform, LightBufferBinding, offset, size);
return *this;
}
PhongGL& PhongGL::bindJointBuffer(GL::Buffer& buffer) {
CORRADE_ASSERT(_flags >= Flag::UniformBuffers,
"Shaders::PhongGL::bindJointBuffer(): the shader was not created with uniform buffers enabled", *this);
buffer.bind(GL::Buffer::Target::Uniform, JointBufferBinding);
return *this;
}
PhongGL& PhongGL::bindJointBuffer(GL::Buffer& buffer, const GLintptr offset, const GLsizeiptr size) {
CORRADE_ASSERT(_flags >= Flag::UniformBuffers,
"Shaders::PhongGL::bindJointBuffer(): the shader was not created with uniform buffers enabled", *this);
buffer.bind(GL::Buffer::Target::Uniform, JointBufferBinding, offset, size);
return *this;
}
#endif
PhongGL& PhongGL::bindAmbientTexture(GL::Texture2D& texture) {
@ -1006,6 +1146,21 @@ PhongGL& PhongGL::bindTextures(GL::Texture2D* ambient, GL::Texture2D* diffuse, G
return *this;
}
#ifndef MAGNUM_TARGET_GLES2
PhongGL::Configuration& PhongGL::Configuration::setJointCount(UnsignedInt count, UnsignedInt perVertexCount, UnsignedInt secondaryPerVertexCount) {
CORRADE_ASSERT(perVertexCount <= 4,
"Shaders::PhongGL::Configuration::setJointCount(): expected at most 4 per-vertex joints, got" << perVertexCount, *this);
CORRADE_ASSERT(secondaryPerVertexCount <= 4,
"Shaders::PhongGL::Configuration::setJointCount(): expected at most 4 secondary per-vertex joints, got" << secondaryPerVertexCount, *this);
CORRADE_ASSERT(!count == (!perVertexCount && !secondaryPerVertexCount),
"Shaders::PhongGL::Configuration::setJointCount(): either both joint count and (secondary) per-vertex joint count has to be non-zero, or all zero", *this);
_jointCount = count;
_perVertexJointCount = perVertexCount;
_secondaryPerVertexJointCount = secondaryPerVertexCount;
return *this;
}
#endif
Debug& operator<<(Debug& debug, const PhongGL::Flag value) {
#ifndef MAGNUM_TARGET_GLES2
/* Special case coming from the Flags printer. As both flags are a superset
@ -1042,6 +1197,9 @@ Debug& operator<<(Debug& debug, const PhongGL::Flag value) {
_c(LightCulling)
#endif
_c(NoSpecular)
#ifndef MAGNUM_TARGET_GLES2
_c(DynamicPerVertexJointCount)
#endif
#undef _c
/* LCOV_EXCL_STOP */
}
@ -1076,7 +1234,10 @@ Debug& operator<<(Debug& debug, const PhongGL::Flags value) {
PhongGL::Flag::TextureArrays,
PhongGL::Flag::LightCulling,
#endif
PhongGL::Flag::NoSpecular
PhongGL::Flag::NoSpecular,
#ifndef MAGNUM_TARGET_GLES2
PhongGL::Flag::DynamicPerVertexJointCount,
#endif
});
}

365
src/Magnum/Shaders/PhongGL.h

@ -225,6 +225,26 @@ The functionality is practically the same as in the @ref FlatGL shader, see
@ref Shaders-FlatGL-object-id "its documentation" for more information and usage
example.
@requires_gl30 Extension @gl_extension{EXT,texture_integer}
@requires_gles30 Object ID output requires integer support in shaders, which
is not available in OpenGL ES 2.0.
@requires_webgl20 Object ID output requires integer support in shaders, which
is not available in WebGL 1.0.
@section Shaders-PhongGL-skinning Skinning
To render skinned meshes, bind up to two sets of up to four-component joint ID
and weight attributes to @ref JointIds / @ref SecondaryJointIds and
@ref Weights / @ref SecondaryWeights, set an appropriate joint count and
per-vertex primary and secondary joint count in
@ref Configuration::setJointCount() and upload appropriate joint matrices with
@ref setJointMatrices().
To avoid having to compile multiple shader variants for different per-vertex
joint counts, enable @ref Flag::DynamicPerVertexJointCount, set the maximum
per-vertex joint count in @ref Configuration::setJointCount() and then adjust
the actual per-draw joint count with @ref setPerVertexJointCount().
@requires_gl30 Extension @gl_extension{EXT,texture_integer}
@requires_gles30 Object ID output requires integer support in shaders, which
is not available in OpenGL ES 2.0.
@ -248,6 +268,12 @@ well to ensure lighting works:
@snippet MagnumShaders-gl.cpp PhongGL-usage-instancing
For instanced skinning the joint buffer is assumed to contain joint
transformations for all instances. By default all instances use the same joint
transformations, seting @ref setPerInstanceJointCount() will cause the shader
to offset the per-vertex joint IDs with
@glsl gl_InstanceID*perInstanceJointCount @ce.
@requires_gl33 Extension @gl_extension{ARB,instanced_arrays}
@requires_gles30 Extension @gl_extension{ANGLE,instanced_arrays},
@gl_extension{EXT,instanced_arrays} or @gl_extension{NV,instanced_arrays}
@ -286,6 +312,17 @@ into the @ref PhongLightUniform array using @ref PhongDrawUniform::lightOffset
and @relativeref{PhongDrawUniform,lightCount}. Besides that, the usage is
similar for all shaders, see @ref shaders-usage-multidraw for an example.
For skinning, joint matrices are supplied via a @ref TransformationUniform3D
buffer bound with @ref bindJointBuffer(). In an instanced scenario the
per-instance joint count is supplied via
@ref PhongDrawUniform::perInstanceJointCount, a per-draw joint offset for the
multidraw scenario is supplied via @ref PhongDrawUniform::jointOffset.
Altogether for a particular draw, each per-vertex joint ID is offset with
@glsl gl_InstanceID*perInstanceJointCount + jointOffset @ce. The
@ref setPerVertexJointCount() stays as an immediate uniform in the UBO and
multidraw scenario as well, as it is tied to a particular mesh layout and thus
doesn't need to vary per draw.
@requires_gl30 Extension @gl_extension{EXT,texture_array} for texture arrays.
@requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} for uniform
buffers.
@ -304,7 +341,8 @@ similar for all shaders, see @ref shaders-usage-multidraw for an example.
*/
class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
public:
class Configuration;
/* MSVC needs dllexport here as well */
class MAGNUM_SHADERS_EXPORT Configuration;
class CompileState;
/**
@ -395,6 +433,64 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
*/
typedef GenericGL3D::Color4 Color4;
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Joint ids
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4ui.
* Used only if @ref perVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::JointIds JointIds;
/**
* @brief Weights
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4.
* Used only if @ref perVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::Weights Weights;
/**
* @brief Secondary joint ids
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4ui.
* Used only if @ref secondaryPerVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::SecondaryJointIds SecondaryJointIds;
/**
* @brief Secondary weights
* @m_since_latest
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Vector4.
* Used only if @ref secondaryPerVertexJointCount() isn't @cpp 0 @ce.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
typedef GenericGL3D::SecondaryWeights SecondaryWeights;
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief (Instanced) object ID
@ -757,7 +853,32 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
* specular highlights are not desired.
* @m_since_latest
*/
NoSpecular = 1 << 16
NoSpecular = 1 << 16,
#ifndef MAGNUM_TARGET_GLES2
/**
* Dynamic per-vertex joint count for skinning. Uses only the first
* M / N primary / secondary components defined by
* @ref setPerVertexJointCount() instead of
* all primary / secondary components defined by
* @ref Configuration::setJointCount() at shader compilation time.
* Useful in order to avoid having a shader permutation defined for
* every possible joint count. Unfortunately it's not possible to
* make use of default values for unspecified input components as
* the last component is always @cpp 1.0 @ce instead of
* @cpp 0.0 @ce, on the other hand dynamically limiting the joint
* count can reduce the time spent executing the vertex shader
* compared to going through the full set of per-vertex joints
* always.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders,
* which is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
* @m_since_latest
*/
DynamicPerVertexJointCount = 1 << 18,
#endif
};
/**
@ -898,6 +1019,47 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
*/
UnsignedInt lightCount() const { return _lightCount; }
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Joint count
* @m_since_latest
*
* If @ref Flag::UniformBuffers is not set, this is the number of joint
* matrices accepted by @ref setJointMatrices() / @ref setJointMatrix().
* If @ref Flag::UniformBuffers is set, this is the statically defined
* size of the @ref TransformationUniform3D uniform buffer bound with
* @ref bindJointBuffer().
* @see @ref Configuration::setJointCount()
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt jointCount() const { return _jointCount; }
/**
* @brief Per-vertex joint count
* @m_since_latest
*
* Returns the value set with @ref Configuration::setJointCount(). If
* @ref Flag::DynamicPerVertexJointCount is set, the count can be
* additionally modified per-draw using @ref setPerVertexJointCount().
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt perVertexJointCount() const { return _perVertexJointCount; }
/**
* @brief Secondary per-vertex joint count
* @m_since_latest
*
* Returns the value set with @ref Configuration::setJointCount(). If
* @ref Flag::DynamicPerVertexJointCount is set, the count can be
* additionally modified per-draw using @ref setPerVertexJointCount().
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt secondaryPerVertexJointCount() const { return _secondaryPerVertexJointCount; }
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Material count
@ -928,6 +1090,34 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
UnsignedInt drawCount() const { return _drawCount; }
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Set dynamic per-vertex skinning joint count
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Allows reducing the count of iterated joints for a particular draw
* call, making it possible to use a single shader with meshes that
* contain different count of per-vertex joints. See
* @ref Flag::DynamicPerVertexJointCount for more information. As the
* joint count is tied to the mesh layout, this is a per-draw-call
* setting even in case of @ref Flag::UniformBuffers instead of being
* a value in @ref PhongDrawUniform. Initial value is the same as
* @ref perVertexJointCount() and @ref secondaryPerVertexJointCount().
*
* Expects that @ref Flag::DynamicPerVertexJointCount is set,
* @p count is not larger than @ref perVertexJointCount() and
* @p secondaryCount not larger than @ref secondaryPerVertexJointCount().
* @see @ref Configuration::setJointCount()
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders, which
* is not available in WebGL 1.0.
*/
PhongGL& setPerVertexJointCount(UnsignedInt count, UnsignedInt secondaryCount = 0);
#endif
/** @{
* @name Uniform setters
*
@ -1393,6 +1583,76 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
*/
PhongGL& setLightRange(UnsignedInt id, Float range);
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Set joint matrices
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Initial values are identity transformations. Expects that the size
* of the @p matrices array is the same as @ref jointCount().
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref TransformationUniform3D::transformationMatrix and call
* @ref bindJointBuffer() instead.
* @see @ref setJointMatrix(UnsignedInt, const Matrix4&)
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
PhongGL& setJointMatrices(const Containers::ArrayView<const Matrix4> matrices);
/**
* @overload
* @m_since_latest
*/
PhongGL& setJointMatrices(std::initializer_list<Matrix4> matrices);
/**
* @brief Set joint matrix for given joint
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Unlike @ref setJointMatrices() updates just a single joint matrix.
* Expects that @p id is less than @ref jointCount().
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref TransformationUniform3D::transformationMatrix and call
* @ref bindJointBuffer() instead.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
PhongGL& setJointMatrix(UnsignedInt id, const Matrix4& matrix);
/**
* @brief Set per-instance joint count
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Offset added to joint IDs in the @ref JointIds and
* @ref SecondaryJointIds in instanced draws. Should be less than
* @ref jointCount(). Initial value is @cpp 0 @ce, meaning every
* instance will use the same joint matrices, setting it to a non-zero
* value causes the joint IDs to be interpreted as
* @glsl gl_InstanceID*count + jointId @ce.
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref PhongDrawUniform::perInstanceJointCount and call
* @ref bindDrawBuffer() instead.
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
PhongGL& setPerInstanceJointCount(UnsignedInt count);
#endif
/**
* @}
*/
@ -1551,6 +1811,25 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
*/
PhongGL& bindLightBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size);
/**
* @brief Bind a joint matrix uniform buffer
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Expects that @ref Flag::UniformBuffers is set. The buffer is
* expected to contain @ref jointCount() instances of
* @ref TransformationUniform3D.
* @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object}
* @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0.
* @requires_webgl20 Uniform buffers are not available in WebGL 1.0.
*/
PhongGL& bindJointBuffer(GL::Buffer& buffer);
/**
* @overload
* @m_since_latest
*/
PhongGL& bindJointBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size);
/**
* @}
*/
@ -1776,7 +2055,11 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
Flags _flags;
UnsignedInt _lightCount{};
#ifndef MAGNUM_TARGET_GLES2
UnsignedInt _materialCount{}, _drawCount{};
UnsignedInt _jointCount{},
_perVertexJointCount{},
_secondaryPerVertexJointCount{},
_materialCount{},
_drawCount{};
#endif
Int _transformationMatrixUniform{0},
_projectionMatrixUniform{1},
@ -1799,9 +2082,13 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
_lightSpecularColorsUniform, /* 12 + 2*lightCount */
_lightRangesUniform; /* 12 + 3*lightCount */
#ifndef MAGNUM_TARGET_GLES2
/* Used instead of all other uniforms when Flag::UniformBuffers is set,
so it can alias them */
Int _drawOffsetUniform{0};
Int _jointMatricesUniform, /* 12 + 4*lightCount */
_perInstanceJointCountUniform, /* 12 + 4*lightCount + jointCount */
/* Used instead of all other uniforms when Flag::UniformBuffers is
set, so it can alias them */
_drawOffsetUniform{0},
/* 13 + 4*lightCount + jointCount, or 1 with UBOs */
_perVertexJointCountUniform;
#endif
};
@ -1811,7 +2098,7 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
@see @ref PhongGL(const Configuration&), @ref compile(const Configuration&)
*/
class PhongGL::Configuration {
class MAGNUM_SHADERS_EXPORT PhongGL::Configuration {
public:
explicit Configuration() = default;
@ -1860,6 +2147,65 @@ class PhongGL::Configuration {
}
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Joint count
*
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt jointCount() const { return _jointCount; }
/**
* @brief Per-vertex joint count
*
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt perVertexJointCount() const { return _perVertexJointCount; }
/**
*@brief Secondary per-vertex joint count
*
* @requires_gles30 Not defined on OpenGL ES 2.0 builds.
* @requires_webgl20 Not defined on WebGL 1.0 builds.
*/
UnsignedInt secondaryPerVertexJointCount() const { return _secondaryPerVertexJointCount; }
/**
* @brief Set joint count
*
* If @ref Flag::UniformBuffers isn't set, @p count describes how many
* joint matrices get supplied to each draw by @ref setJointMatrices()
* / @ref setJointMatrix(). If @ref Flag::UniformBuffers is set,
* @p count describes size of a @ref TransformationUniform3D buffer
* bound with @ref bindJointBuffer(); as uniform buffers are required
* to have a statically defined size. The per-vertex joints then index
* into the array offset by @ref PhongDrawUniform::jointOffset. If
* @p count is @cpp 0 @ce, skinning is not performed.
*
* The @p perVertexCount and @p secondaryPerVertexCount then describe
* how many components are taken from @ref JointIds / @ref Weights and
* @ref SecondaryJointIds / @ref SecondaryWeights attributes. Both
* values are expected to not be larger than @cpp 4 @ce, setting either
* of these to @cpp 0 @ce means given attribute is not used at all. If
* @p count is @cpp 0 @ce, both @p perVertexCount and
* @p secondaryPerVertexCount is expected to be @cpp 0 @ce as well; if
* @p count is non-zero at least one of @p perVertexCount and
* @p secondaryPerVertexCount is expected to be non-zero as well.
*
* Default value for all three is @cpp 0 @ce.
* @see @ref PhongGL::jointCount(), @ref PhongGL::perVertexJointCount(),
* @ref PhongGL::secondaryPerVertexJointCount(),
* @ref Flag::DynamicPerVertexJointCount,
* @ref PhongGL::setPerVertexJointCount()
* @requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
* @requires_gles30 Skinning requires integer support in shaders, which
* is not available in OpenGL ES 2.0.
* @requires_webgl20 Skinning requires integer support in shaders,
* which is not available in WebGL 1.0.
*/
Configuration& setJointCount(UnsignedInt count, UnsignedInt perVertexCount, UnsignedInt secondaryPerVertexCount = 0);
/**
* @brief Material count
*
@ -1925,7 +2271,10 @@ class PhongGL::Configuration {
Flags _flags;
UnsignedInt _lightCount = 1;
#ifndef MAGNUM_TARGET_GLES2
UnsignedInt _materialCount = 1,
UnsignedInt _jointCount = 0,
_perVertexJointCount = 0,
_secondaryPerVertexJointCount = 0,
_materialCount = 1,
_drawCount = 1;
#endif
};

20
src/Magnum/Shaders/Test/CMakeLists.txt

@ -38,10 +38,10 @@ corrade_add_test(ShadersVectorTest VectorTest.cpp LIBRARIES MagnumShaders)
# There's an underscore between GL and Test to disambiguate from GLTest, which
# is a common suffix used to mark tests that need a GL context. Ugly, I know.
corrade_add_test(ShadersDistanceFieldVectorGL_Test DistanceFieldVectorGL_Test.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersFlatGL_Test FlatGL_Test.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersFlatGL_Test FlatGL_Test.cpp LIBRARIES MagnumShadersTestLib)
corrade_add_test(ShadersGenericGL_Test GenericGL_Test.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersMeshVisualizerGL_Test MeshVisualizerGL_Test.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersPhongGL_Test PhongGL_Test.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersMeshVisualizerGL_Test MeshVisualizerGL_Test.cpp LIBRARIES MagnumShadersTestLib)
corrade_add_test(ShadersPhongGL_Test PhongGL_Test.cpp LIBRARIES MagnumShadersTestLib)
corrade_add_test(ShadersVectorGL_Test VectorGL_Test.cpp LIBRARIES MagnumShaders)
corrade_add_test(ShadersVertexColorGL_Test VertexColorGL_Test.cpp LIBRARIES MagnumShaders)
@ -127,6 +127,10 @@ if(MAGNUM_BUILD_GL_TESTS)
TestFiles/diffuse-alpha-texture.tga
TestFiles/diffuse-texture.tga
TestFiles/alpha-mask1.0.tga
TestFiles/skinning.tga
TestFiles/skinning-default.tga
TestFiles/skinning-instanced.tga
TestFiles/skinning-multi.tga
FlatTestFiles/colored2D.tga
FlatTestFiles/colored3D.tga
@ -246,7 +250,11 @@ if(MAGNUM_BUILD_GL_TESTS)
MeshVisualizerTestFiles/multidraw-instancedobjectid2D.tga
MeshVisualizerTestFiles/multidraw-instancedobjectid3D.tga
MeshVisualizerTestFiles/multidraw-objectidtexture2D.tga
MeshVisualizerTestFiles/multidraw-objectidtexture3D.tga)
MeshVisualizerTestFiles/multidraw-objectidtexture3D.tga
MeshVisualizerTestFiles/skinning.tga
MeshVisualizerTestFiles/skinning-default.tga
MeshVisualizerTestFiles/skinning-instanced.tga
MeshVisualizerTestFiles/skinning-multi.tga)
target_include_directories(ShadersMeshVisualizerGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)
if(MAGNUM_BUILD_PLUGINS_STATIC)
if(MAGNUM_WITH_ANYIMAGEIMPORTER)
@ -284,6 +292,10 @@ if(MAGNUM_BUILD_GL_TESTS)
TestFiles/normal-texture.tga
TestFiles/specular-texture.tga
TestFiles/alpha-mask1.0.tga
TestFiles/skinning.tga
TestFiles/skinning-default.tga
TestFiles/skinning-instanced.tga
TestFiles/skinning-multi.tga
PhongTestFiles/colored.tga
PhongTestFiles/defaults.tga

1400
src/Magnum/Shaders/Test/FlatGLTest.cpp

File diff suppressed because it is too large Load Diff

54
src/Magnum/Shaders/Test/FlatGL_Test.cpp

@ -27,7 +27,7 @@
#include <Corrade/Containers/String.h>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/Format.h>
#include <Corrade/Utility/FormatStl.h>
#include "Magnum/Shaders/FlatGL.h"
@ -38,6 +38,10 @@ namespace Magnum { namespace Shaders { namespace Test { namespace {
struct FlatGL_Test: TestSuite::Tester {
explicit FlatGL_Test();
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void configurationSetJointCountInvalid();
#endif
template<UnsignedInt dimensions> void constructNoCreate();
template<UnsignedInt dimensions> void constructCopy();
@ -46,7 +50,38 @@ struct FlatGL_Test: TestSuite::Tester {
void debugFlagsSupersets();
};
#ifndef MAGNUM_TARGET_GLES2
const struct {
const char* name;
UnsignedInt jointCount, perVertexJointCount, secondaryPerVertexJointCount;
const char* message;
} ConfigurationSetJointCountInvalidData[] {
{"per-vertex joint count too large",
10, 5, 0,
"expected at most 4 per-vertex joints, got 5"},
{"secondary per-vertex joint count too large",
10, 0, 5,
"expected at most 4 secondary per-vertex joints, got 5"},
{"joint count but no per-vertex joint count",
10, 0, 0,
"either both joint count and (secondary) per-vertex joint count has to be non-zero, or all zero"},
{"per-vertex joint count but no joint count",
0, 2, 0,
"either both joint count and (secondary) per-vertex joint count has to be non-zero, or all zero"},
{"secondary per-vertex joint count but no joint count",
0, 0, 3,
"either both joint count and (secondary) per-vertex joint count has to be non-zero, or all zero"},
};
#endif
FlatGL_Test::FlatGL_Test() {
#ifndef MAGNUM_TARGET_GLES2
addInstancedTests<FlatGL_Test>({
&FlatGL_Test::configurationSetJointCountInvalid<2>,
&FlatGL_Test::configurationSetJointCountInvalid<3>},
Containers::arraySize(ConfigurationSetJointCountInvalidData));
#endif
addTests({&FlatGL_Test::constructNoCreate<2>,
&FlatGL_Test::constructNoCreate<3>,
@ -58,6 +93,23 @@ FlatGL_Test::FlatGL_Test() {
&FlatGL_Test::debugFlagsSupersets});
}
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void FlatGL_Test::configurationSetJointCountInvalid() {
auto&& data = ConfigurationSetJointCountInvalidData[testCaseInstanceId()];
setTestCaseDescription(data.name);
setTestCaseTemplateName(Utility::format("{}", dimensions));
CORRADE_SKIP_IF_NO_ASSERT();
typename FlatGL<dimensions>::Configuration configuration;
std::ostringstream out;
Error redirectError{&out};
configuration.setJointCount(data.jointCount, data.perVertexJointCount, data.secondaryPerVertexJointCount);
CORRADE_COMPARE(out.str(), Utility::formatString("Shaders::FlatGL::Configuration::setJointCount(): {}\n", data.message));
}
#endif
template<UnsignedInt dimensions> void FlatGL_Test::constructNoCreate() {
setTestCaseTemplateName(Utility::format("{}", dimensions));

26
src/Magnum/Shaders/Test/FlatTest.cpp

@ -90,6 +90,10 @@ void FlatTest::drawUniformConstructDefault() {
CORRADE_COMPARE(b.materialId, 0);
CORRADE_COMPARE(a.objectId, 0);
CORRADE_COMPARE(b.objectId, 0);
CORRADE_COMPARE(a.jointOffset, 0);
CORRADE_COMPARE(b.jointOffset, 0);
CORRADE_COMPARE(a.perInstanceJointCount, 0);
CORRADE_COMPARE(b.perInstanceJointCount, 0);
constexpr FlatDrawUniform ca;
constexpr FlatDrawUniform cb{DefaultInit};
@ -97,6 +101,10 @@ void FlatTest::drawUniformConstructDefault() {
CORRADE_COMPARE(cb.materialId, 0);
CORRADE_COMPARE(ca.objectId, 0);
CORRADE_COMPARE(cb.objectId, 0);
CORRADE_COMPARE(ca.jointOffset, 0);
CORRADE_COMPARE(cb.jointOffset, 0);
CORRADE_COMPARE(ca.perInstanceJointCount, 0);
CORRADE_COMPARE(cb.perInstanceJointCount, 0);
CORRADE_VERIFY(std::is_nothrow_default_constructible<FlatDrawUniform>::value);
CORRADE_VERIFY(std::is_nothrow_constructible<FlatDrawUniform, DefaultInitT>::value);
@ -110,6 +118,7 @@ void FlatTest::drawUniformConstructNoInit() {
FlatDrawUniform a;
a.materialId = 5;
a.objectId = 7;
a.perInstanceJointCount = 9;
new(&a) FlatDrawUniform{NoInit};
{
@ -121,6 +130,7 @@ void FlatTest::drawUniformConstructNoInit() {
#endif
CORRADE_COMPARE(a.materialId, 5);
CORRADE_COMPARE(a.objectId, 7);
CORRADE_COMPARE(a.perInstanceJointCount, 9);
}
CORRADE_VERIFY(std::is_nothrow_constructible<FlatDrawUniform, NoInitT>::value);
@ -132,17 +142,29 @@ void FlatTest::drawUniformConstructNoInit() {
void FlatTest::drawUniformSetters() {
FlatDrawUniform a;
a.setMaterialId(5)
.setObjectId(7);
.setObjectId(7)
.setJointOffset(6)
.setPerInstanceJointCount(8);
CORRADE_COMPARE(a.materialId, 5);
CORRADE_COMPARE(a.objectId, 7);
CORRADE_COMPARE(a.jointOffset, 6);
CORRADE_COMPARE(a.perInstanceJointCount, 8);
}
void FlatTest::drawUniformMaterialIdPacking() {
FlatDrawUniform a;
a.setMaterialId(13765);
a.setMaterialId(13765)
/* second 16 bits unused */
.setJointOffset(13767)
.setPerInstanceJointCount(63574);
/* materialId should be right at the beginning, in the low 16 bits on both
LE and BE */
CORRADE_COMPARE(reinterpret_cast<UnsignedInt*>(&a)[0] & 0xffff, 13765);
/* second 16 bits unused */
/* jointOffset in the low, perInstanceJointCount in the high */
CORRADE_COMPARE(reinterpret_cast<UnsignedInt*>(&a)[2] & 0xffff, 13767);
CORRADE_COMPARE((reinterpret_cast<UnsignedInt*>(&a)[2] >> 16) & 0xffff, 63574);
}
void FlatTest::materialUniformConstructDefault() {

1878
src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp

File diff suppressed because it is too large Load Diff

67
src/Magnum/Shaders/Test/MeshVisualizerGL_Test.cpp

@ -26,6 +26,7 @@
#include <sstream>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/FormatStl.h>
#include "Magnum/Shaders/MeshVisualizerGL.h"
@ -36,6 +37,11 @@ namespace Magnum { namespace Shaders { namespace Test { namespace {
struct MeshVisualizerGL_Test: TestSuite::Tester {
explicit MeshVisualizerGL_Test();
#ifndef MAGNUM_TARGET_GLES2
void configurationSetJointCountInvalid2D();
void configurationSetJointCountInvalid3D();
#endif
void constructNoCreate2D();
void constructNoCreate3D();
@ -54,7 +60,38 @@ struct MeshVisualizerGL_Test: TestSuite::Tester {
#endif
};
#ifndef MAGNUM_TARGET_GLES2
const struct {
const char* name;
UnsignedInt jointCount, perVertexJointCount, secondaryPerVertexJointCount;
const char* message;
} ConfigurationSetJointCountInvalidData[] {
{"per-vertex joint count too large",
10, 5, 0,
"expected at most 4 per-vertex joints, got 5"},
{"secondary per-vertex joint count too large",
10, 0, 5,
"expected at most 4 secondary per-vertex joints, got 5"},
{"joint count but no per-vertex joint count",
10, 0, 0,
"either both joint count and (secondary) per-vertex joint count has to be non-zero, or all zero"},
{"per-vertex joint count but no joint count",
0, 2, 0,
"either both joint count and (secondary) per-vertex joint count has to be non-zero, or all zero"},
{"secondary per-vertex joint count but no joint count",
0, 0, 3,
"either both joint count and (secondary) per-vertex joint count has to be non-zero, or all zero"},
};
#endif
MeshVisualizerGL_Test::MeshVisualizerGL_Test() {
#ifndef MAGNUM_TARGET_GLES2
addInstancedTests({
&MeshVisualizerGL_Test::configurationSetJointCountInvalid2D,
&MeshVisualizerGL_Test::configurationSetJointCountInvalid3D},
Containers::arraySize(ConfigurationSetJointCountInvalidData));
#endif
addTests({
&MeshVisualizerGL_Test::constructNoCreate2D,
&MeshVisualizerGL_Test::constructNoCreate3D,
@ -75,6 +112,36 @@ MeshVisualizerGL_Test::MeshVisualizerGL_Test() {
});
}
#ifndef MAGNUM_TARGET_GLES2
void MeshVisualizerGL_Test::configurationSetJointCountInvalid2D() {
auto&& data = ConfigurationSetJointCountInvalidData[testCaseInstanceId()];
setTestCaseDescription(data.name);
CORRADE_SKIP_IF_NO_ASSERT();
MeshVisualizerGL2D::Configuration configuration;
std::ostringstream out;
Error redirectError{&out};
configuration.setJointCount(data.jointCount, data.perVertexJointCount, data.secondaryPerVertexJointCount);
CORRADE_COMPARE(out.str(), Utility::formatString("Shaders::MeshVisualizerGL2D::Configuration::setJointCount(): {}\n", data.message));
}
void MeshVisualizerGL_Test::configurationSetJointCountInvalid3D() {
auto&& data = ConfigurationSetJointCountInvalidData[testCaseInstanceId()];
setTestCaseDescription(data.name);
CORRADE_SKIP_IF_NO_ASSERT();
MeshVisualizerGL3D::Configuration configuration;
std::ostringstream out;
Error redirectError{&out};
configuration.setJointCount(data.jointCount, data.perVertexJointCount, data.secondaryPerVertexJointCount);
CORRADE_COMPARE(out.str(), Utility::formatString("Shaders::MeshVisualizerGL3D::Configuration::setJointCount(): {}\n", data.message));
}
#endif
void MeshVisualizerGL_Test::constructNoCreate2D() {
{
MeshVisualizerGL2D shader{NoCreate};

52
src/Magnum/Shaders/Test/MeshVisualizerTest.cpp

@ -105,6 +105,10 @@ void MeshVisualizerTest::drawUniform2DConstructDefault() {
CORRADE_COMPARE(b.materialId, 0);
CORRADE_COMPARE(a.objectId, 0);
CORRADE_COMPARE(b.objectId, 0);
CORRADE_COMPARE(a.jointOffset, 0);
CORRADE_COMPARE(b.jointOffset, 0);
CORRADE_COMPARE(a.perInstanceJointCount, 0);
CORRADE_COMPARE(b.perInstanceJointCount, 0);
constexpr MeshVisualizerDrawUniform2D ca;
constexpr MeshVisualizerDrawUniform2D cb{DefaultInit};
@ -112,6 +116,10 @@ void MeshVisualizerTest::drawUniform2DConstructDefault() {
CORRADE_COMPARE(cb.materialId, 0);
CORRADE_COMPARE(ca.objectId, 0);
CORRADE_COMPARE(cb.objectId, 0);
CORRADE_COMPARE(ca.jointOffset, 0);
CORRADE_COMPARE(cb.jointOffset, 0);
CORRADE_COMPARE(ca.perInstanceJointCount, 0);
CORRADE_COMPARE(cb.perInstanceJointCount, 0);
CORRADE_VERIFY(std::is_nothrow_default_constructible<MeshVisualizerDrawUniform2D>::value);
CORRADE_VERIFY(std::is_nothrow_constructible<MeshVisualizerDrawUniform2D, DefaultInitT>::value);
@ -125,6 +133,7 @@ void MeshVisualizerTest::drawUniform2DConstructNoInit() {
MeshVisualizerDrawUniform2D a;
a.materialId = 73;
a.objectId = 7;
a.perInstanceJointCount = 9;
new(&a) MeshVisualizerDrawUniform2D{NoInit};
{
@ -136,6 +145,7 @@ void MeshVisualizerTest::drawUniform2DConstructNoInit() {
#endif
CORRADE_COMPARE(a.materialId, 73);
CORRADE_COMPARE(a.objectId, 7);
CORRADE_COMPARE(a.perInstanceJointCount, 9);
}
CORRADE_VERIFY(std::is_nothrow_constructible<MeshVisualizerDrawUniform2D, NoInitT>::value);
@ -147,17 +157,29 @@ void MeshVisualizerTest::drawUniform2DConstructNoInit() {
void MeshVisualizerTest::drawUniform2DSetters() {
MeshVisualizerDrawUniform2D a;
a.setMaterialId(73)
.setObjectId(7);
.setObjectId(7)
.setJointOffset(6)
.setPerInstanceJointCount(8);
CORRADE_COMPARE(a.materialId, 73);
CORRADE_COMPARE(a.objectId, 7);
CORRADE_COMPARE(a.jointOffset, 6);
CORRADE_COMPARE(a.perInstanceJointCount, 8);
}
void MeshVisualizerTest::drawUniform2DMaterialIdPacking() {
MeshVisualizerDrawUniform2D a;
a.setMaterialId(13765);
a.setMaterialId(13765)
/* second 16 bits unused */
.setJointOffset(13767)
.setPerInstanceJointCount(63574);
/* materialId should be right at the beginning, in the low 16 bits on both
LE and BE */
CORRADE_COMPARE(reinterpret_cast<UnsignedInt*>(&a)[0] & 0xffff, 13765);
/* second 16 bits unused */
/* jointOffset in the low, perInstanceJointCount in the high */
CORRADE_COMPARE(reinterpret_cast<UnsignedInt*>(&a)[2] & 0xffff, 13767);
CORRADE_COMPARE((reinterpret_cast<UnsignedInt*>(&a)[2] >> 16) & 0xffff, 63574);
}
void MeshVisualizerTest::drawUniform3DConstructDefault() {
@ -177,6 +199,10 @@ void MeshVisualizerTest::drawUniform3DConstructDefault() {
CORRADE_COMPARE(b.materialId, 0);
CORRADE_COMPARE(a.objectId, 0);
CORRADE_COMPARE(b.objectId, 0);
CORRADE_COMPARE(a.jointOffset, 0);
CORRADE_COMPARE(b.jointOffset, 0);
CORRADE_COMPARE(a.perInstanceJointCount, 0);
CORRADE_COMPARE(b.perInstanceJointCount, 0);
constexpr MeshVisualizerDrawUniform3D ca;
constexpr MeshVisualizerDrawUniform3D cb{DefaultInit};
@ -194,6 +220,10 @@ void MeshVisualizerTest::drawUniform3DConstructDefault() {
CORRADE_COMPARE(cb.materialId, 0);
CORRADE_COMPARE(ca.objectId, 0);
CORRADE_COMPARE(cb.objectId, 0);
CORRADE_COMPARE(ca.jointOffset, 0);
CORRADE_COMPARE(cb.jointOffset, 0);
CORRADE_COMPARE(ca.perInstanceJointCount, 0);
CORRADE_COMPARE(cb.perInstanceJointCount, 0);
CORRADE_VERIFY(std::is_nothrow_default_constructible<MeshVisualizerDrawUniform3D>::value);
CORRADE_VERIFY(std::is_nothrow_constructible<MeshVisualizerDrawUniform3D, DefaultInitT>::value);
@ -208,6 +238,7 @@ void MeshVisualizerTest::drawUniform3DConstructNoInit() {
a.normalMatrix[2] = {1.5f, 0.3f, 3.1f, 0.5f};
a.materialId = 5;
a.objectId = 7;
a.perInstanceJointCount = 9;
new(&a) MeshVisualizerDrawUniform3D{NoInit};
{
@ -220,6 +251,7 @@ void MeshVisualizerTest::drawUniform3DConstructNoInit() {
CORRADE_COMPARE(a.normalMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f}));
CORRADE_COMPARE(a.materialId, 5);
CORRADE_COMPARE(a.objectId, 7);
CORRADE_COMPARE(a.perInstanceJointCount, 9);
}
CORRADE_VERIFY(std::is_nothrow_constructible<MeshVisualizerDrawUniform3D, NoInitT>::value);
@ -232,7 +264,9 @@ void MeshVisualizerTest::drawUniform3DSetters() {
MeshVisualizerDrawUniform3D a;
a.setNormalMatrix(Matrix4::rotationX(90.0_degf).normalMatrix())
.setMaterialId(5)
.setObjectId(7);
.setObjectId(7)
.setJointOffset(6)
.setPerInstanceJointCount(8);
CORRADE_COMPARE(a.normalMatrix, (Matrix3x4{
Vector4{1.0f, 0.0f, 0.0f, 0.0f},
Vector4{0.0f, 0.0f, 1.0f, 0.0f},
@ -240,14 +274,24 @@ void MeshVisualizerTest::drawUniform3DSetters() {
}));
CORRADE_COMPARE(a.materialId, 5);
CORRADE_COMPARE(a.objectId, 7);
CORRADE_COMPARE(a.jointOffset, 6);
CORRADE_COMPARE(a.perInstanceJointCount, 8);
}
void MeshVisualizerTest::drawUniform3DMaterialIdPacking() {
MeshVisualizerDrawUniform3D a;
a.setMaterialId(13765);
a.setMaterialId(13765)
/* second 16 bits unused */
.setJointOffset(13767)
.setPerInstanceJointCount(63574);
/* The normalMatrix field is 3x4 floats, materialId should be right after
in the low 16 bits on both LE and BE */
CORRADE_COMPARE(reinterpret_cast<UnsignedInt*>(&a)[12] & 0xffff, 13765);
/* second 16 bits unused */
/* jointOffset in the low, perInstanceJointCount in the high */
CORRADE_COMPARE(reinterpret_cast<UnsignedInt*>(&a)[14] & 0xffff, 13767);
CORRADE_COMPARE((reinterpret_cast<UnsignedInt*>(&a)[14] >> 16) & 0xffff, 63574);
}
void MeshVisualizerTest::materialUniformConstructDefault() {

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/skinning-default.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/skinning-instanced.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/skinning-multi.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/skinning.tga

Binary file not shown.

937
src/Magnum/Shaders/Test/PhongGLTest.cpp

File diff suppressed because it is too large Load Diff

50
src/Magnum/Shaders/Test/PhongGL_Test.cpp

@ -26,6 +26,7 @@
#include <sstream>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/FormatStl.h>
#include "Magnum/Shaders/PhongGL.h"
@ -36,6 +37,10 @@ namespace Magnum { namespace Shaders { namespace Test { namespace {
struct PhongGL_Test: TestSuite::Tester {
explicit PhongGL_Test();
#ifndef MAGNUM_TARGET_GLES2
void configurationSetJointCountInvalid();
#endif
void constructNoCreate();
void constructCopy();
@ -44,7 +49,36 @@ struct PhongGL_Test: TestSuite::Tester {
void debugFlagsSupersets();
};
#ifndef MAGNUM_TARGET_GLES2
const struct {
const char* name;
UnsignedInt jointCount, perVertexJointCount, secondaryPerVertexJointCount;
const char* message;
} ConfigurationSetJointCountInvalidData[] {
{"per-vertex joint count too large",
10, 5, 0,
"expected at most 4 per-vertex joints, got 5"},
{"secondary per-vertex joint count too large",
10, 0, 5,
"expected at most 4 secondary per-vertex joints, got 5"},
{"joint count but no per-vertex joint count",
10, 0, 0,
"either both joint count and (secondary) per-vertex joint count has to be non-zero, or all zero"},
{"per-vertex joint count but no joint count",
0, 2, 0,
"either both joint count and (secondary) per-vertex joint count has to be non-zero, or all zero"},
{"secondary per-vertex joint count but no joint count",
0, 0, 3,
"either both joint count and (secondary) per-vertex joint count has to be non-zero, or all zero"},
};
#endif
PhongGL_Test::PhongGL_Test() {
#ifndef MAGNUM_TARGET_GLES2
addInstancedTests({&PhongGL_Test::configurationSetJointCountInvalid},
Containers::arraySize(ConfigurationSetJointCountInvalidData));
#endif
addTests({&PhongGL_Test::constructNoCreate,
&PhongGL_Test::constructCopy,
@ -53,6 +87,22 @@ PhongGL_Test::PhongGL_Test() {
&PhongGL_Test::debugFlagsSupersets});
}
#ifndef MAGNUM_TARGET_GLES2
void PhongGL_Test::configurationSetJointCountInvalid() {
auto&& data = ConfigurationSetJointCountInvalidData[testCaseInstanceId()];
setTestCaseDescription(data.name);
CORRADE_SKIP_IF_NO_ASSERT();
PhongGL::Configuration configuration;
std::ostringstream out;
Error redirectError{&out};
configuration.setJointCount(data.jointCount, data.perVertexJointCount, data.secondaryPerVertexJointCount);
CORRADE_COMPARE(out.str(), Utility::formatString("Shaders::PhongGL::Configuration::setJointCount(): {}\n", data.message));
}
#endif
void PhongGL_Test::constructNoCreate() {
{
PhongGL shader{NoCreate};

24
src/Magnum/Shaders/Test/PhongTest.cpp

@ -117,6 +117,10 @@ void PhongTest::drawUniformConstructDefault() {
CORRADE_COMPARE(b.lightOffset, 0);
CORRADE_COMPARE(a.lightCount, 0xffffu);
CORRADE_COMPARE(b.lightCount, 0xffffu);
CORRADE_COMPARE(a.jointOffset, 0);
CORRADE_COMPARE(b.jointOffset, 0);
CORRADE_COMPARE(a.perInstanceJointCount, 0);
CORRADE_COMPARE(b.perInstanceJointCount, 0);
constexpr PhongDrawUniform ca;
constexpr PhongDrawUniform cb{DefaultInit};
@ -138,6 +142,10 @@ void PhongTest::drawUniformConstructDefault() {
CORRADE_COMPARE(cb.lightOffset, 0);
CORRADE_COMPARE(ca.lightCount, 0xffffu);
CORRADE_COMPARE(cb.lightCount, 0xffffu);
CORRADE_COMPARE(ca.jointOffset, 0);
CORRADE_COMPARE(cb.jointOffset, 0);
CORRADE_COMPARE(ca.perInstanceJointCount, 0);
CORRADE_COMPARE(cb.perInstanceJointCount, 0);
CORRADE_VERIFY(std::is_nothrow_default_constructible<PhongDrawUniform>::value);
CORRADE_VERIFY(std::is_nothrow_constructible<PhongDrawUniform, DefaultInitT>::value);
@ -152,6 +160,7 @@ void PhongTest::drawUniformConstructNoInit() {
a.normalMatrix[2] = {1.5f, 0.3f, 3.1f, 0.5f};
a.materialId = 5;
a.lightCount = 7;
a.perInstanceJointCount = 9;
new(&a) PhongDrawUniform{NoInit};
{
@ -164,6 +173,7 @@ void PhongTest::drawUniformConstructNoInit() {
CORRADE_COMPARE(a.normalMatrix[2], (Vector4{1.5f, 0.3f, 3.1f, 0.5f}));
CORRADE_COMPARE(a.materialId, 5);
CORRADE_COMPARE(a.lightCount, 7);
CORRADE_COMPARE(a.perInstanceJointCount, 9);
}
CORRADE_VERIFY(std::is_nothrow_constructible<PhongDrawUniform, NoInitT>::value);
@ -177,7 +187,9 @@ void PhongTest::drawUniformSetters() {
a.setNormalMatrix(Matrix4::rotationX(90.0_degf).normalMatrix())
.setMaterialId(5)
.setObjectId(7)
.setLightOffsetCount(9, 13);
.setLightOffsetCount(9, 13)
.setJointOffset(6)
.setPerInstanceJointCount(8);
CORRADE_COMPARE(a.normalMatrix, (Matrix3x4{
Vector4{1.0f, 0.0f, 0.0f, 0.0f},
Vector4{0.0f, 0.0f, 1.0f, 0.0f},
@ -187,13 +199,17 @@ void PhongTest::drawUniformSetters() {
CORRADE_COMPARE(a.objectId, 7);
CORRADE_COMPARE(a.lightOffset, 9);
CORRADE_COMPARE(a.lightCount, 13);
CORRADE_COMPARE(a.jointOffset, 6);
CORRADE_COMPARE(a.perInstanceJointCount, 8);
}
void PhongTest::drawUniformPacking() {
PhongDrawUniform a;
a.setMaterialId(13765)
/* second 16 bits unused */
.setLightOffsetCount(13766, 63573);
.setLightOffsetCount(13766, 63573)
.setJointOffset(13767)
.setPerInstanceJointCount(63574);
/* The normalMatrix field is 3x4 floats, materialId should be right after
in the low 16 bits on both LE and BE */
CORRADE_COMPARE(reinterpret_cast<UnsignedInt*>(&a)[12] & 0xffff, 13765);
@ -203,6 +219,10 @@ void PhongTest::drawUniformPacking() {
in the high */
CORRADE_COMPARE(reinterpret_cast<UnsignedInt*>(&a)[14] & 0xffff, 13766);
CORRADE_COMPARE((reinterpret_cast<UnsignedInt*>(&a)[14] >> 16) & 0xffff, 63573);
/* jointOffset in the low, perInstanceJointCount in the high */
CORRADE_COMPARE(reinterpret_cast<UnsignedInt*>(&a)[15] & 0xffff, 13767);
CORRADE_COMPARE((reinterpret_cast<UnsignedInt*>(&a)[15] >> 16) & 0xffff, 63574);
}
void PhongTest::materialUniformConstructDefault() {

BIN
src/Magnum/Shaders/Test/TestFiles/skinning-default.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/TestFiles/skinning-instanced.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/TestFiles/skinning-multi.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/TestFiles/skinning.tga

Binary file not shown.
Loading…
Cancel
Save