diff --git a/src/Magnum/Shaders/Generic.h b/src/Magnum/Shaders/Generic.h index fb8db297c..30d18e738 100644 --- a/src/Magnum/Shaders/Generic.h +++ b/src/Magnum/Shaders/Generic.h @@ -470,10 +470,10 @@ struct BaseGeneric { typedef GL::Attribute<4, UnsignedInt> ObjectId; #endif typedef GL::Attribute<6, Vector4> Weights; - typedef GL::Attribute<7, Vector4> JointIds; + typedef GL::Attribute<7, Vector4ui> JointIds; typedef GL::Attribute<10, Vector4> SecondaryWeights; - typedef GL::Attribute<11, Vector4> SecondaryJointIds; + typedef GL::Attribute<11, Vector4ui> SecondaryJointIds; typedef GL::Attribute<15, Vector2> TextureOffset; diff --git a/src/Magnum/Shaders/Phong.cpp b/src/Magnum/Shaders/Phong.cpp index 72238fb2c..f11cb2a9e 100644 --- a/src/Magnum/Shaders/Phong.cpp +++ b/src/Magnum/Shaders/Phong.cpp @@ -54,9 +54,13 @@ namespace { }; } -Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _lightCount{lightCount}, _lightColorsUniform{_lightPositionsUniform + Int(lightCount)} { +Phong::Phong(const Flags flags, const UnsignedInt lightCount, const UnsignedInt jointCount, const UnsignedInt jointsPerVertex): _flags{flags}, _lightCount{lightCount}, _jointCount{jointCount}, _jointsPerVertex{jointsPerVertex}, _lightColorsUniform{_lightPositionsUniform + Int(lightCount)}, _jointMatricesUniform{_lightPositionsUniform + 2*Int(lightCount)} { CORRADE_ASSERT(!(flags & Flag::TextureTransformation) || (flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture)), "Shaders::Phong: texture transformation enabled but the shader is not textured", ); + CORRADE_ASSERT(!(flags & Flag::Skinning) || (jointCount != 0), + "Shaders::Phong: skinning enabled, but jointCount is zero", ); + CORRADE_ASSERT(!(flags & Flag::Skinning) || (jointsPerVertex > 0 && jointsPerVertex <= 8), + "Shaders::Phong: skinning enabled, but jointsPerVertex is not in [1;8]", ); #ifdef MAGNUM_BUILD_STATIC /* Import resources on static build, if not already */ @@ -120,7 +124,12 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l #endif .addSource(Utility::formatString( "#define LIGHT_COUNT {}\n" - "#define LIGHT_COLORS_LOCATION {}\n", lightCount, _lightPositionsUniform + lightCount)); + "#define LIGHT_COLORS_LOCATION {}\n", lightCount, _lightColorsUniform)) + .addSource(flags & Flag::Skinning ? "#define SKINNING\n" : "") + .addSource(Utility::formatString( + "#define JOINT_COUNT {}\n" + "#define JOINTS_PER_VERTEX {}\n" + "#define JOINT_MATRICES_LOCATION {}\n", jointCount, jointsPerVertex, _jointMatricesUniform)); #ifndef MAGNUM_TARGET_GLES if(lightCount) frag.addSource(std::move(lightInitializer)); #endif @@ -159,6 +168,15 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l if(flags >= Flag::InstancedTextureOffset) bindAttributeLocation(TextureOffset::Location, "instancedTextureOffset"); #endif + if(flags & Flag::Skinning) { + bindAttributeLocation(Weights::Location, "weights"); + bindAttributeLocation(JointIds::Location, "jointIds"); + + if(jointCount > 4) { + bindAttributeLocation(SecondaryWeights::Location, "secondaryWeights"); + bindAttributeLocation(SecondaryJointIds::Location, "secondaryJointIds"); + } + } } #endif @@ -185,6 +203,8 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l #ifndef MAGNUM_TARGET_GLES2 if(flags & Flag::ObjectId) _objectIdUniform = uniformLocation("objectId"); #endif + if(flags & Flag::Skinning) + _jointMatricesUniform = uniformLocation("jointMatrices"); } #ifndef MAGNUM_TARGET_GLES @@ -353,6 +373,26 @@ Phong& Phong::setLightColor(UnsignedInt id, const Magnum::Color4& color) { return *this; } +Phong& Phong::setJointMatrices(const Containers::ArrayView matrices) { + CORRADE_ASSERT(_jointCount == matrices.size(), + "Shaders::Phong::setJointMatrices(): expected" << _jointCount << "items but got" << matrices.size(), *this); + if(_jointCount) setUniform(_jointMatricesUniform, matrices); + return *this; +} + +Phong& Phong::setJointMatrix(UnsignedInt id, const Matrix4& matrix) { + CORRADE_ASSERT(id < _jointCount, + "Shaders::Phong::setJointMatrix(): joint ID" << id << "is out of bounds for" << _jointCount << "joints", *this); + setUniform(_jointMatricesUniform + id, matrix); + return *this; +} + +/* It's light, but can't be in the header because MSVC needs to know the size + of Matrix4 for the initializer list use */ +Phong& Phong::setJointMatrices(std::initializer_list matrices) { + return setJointMatrices({matrices.begin(), matrices.size()}); +} + Debug& operator<<(Debug& debug, const Phong::Flag value) { debug << "Shaders::Phong::Flag" << Debug::nospace; diff --git a/src/Magnum/Shaders/Phong.h b/src/Magnum/Shaders/Phong.h index fc75fddb0..ecac4da0e 100644 --- a/src/Magnum/Shaders/Phong.h +++ b/src/Magnum/Shaders/Phong.h @@ -207,6 +207,42 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { */ typedef Generic3D::Color4 Color4; + /** + * @brief Joint ids + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", @ref Magnum::Vector4ui. + * Used only if @ref Flag::Skinning is set. + */ + typedef Generic3D::JointIds JointIds; + + /** + * @brief Weights + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", @ref Magnum::Vector4. + * Used only if @ref Flag::Skinning is set. + */ + typedef Generic3D::Weights Weights; + + /** + * @brief Secondary joint ids + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", @ref Magnum::Vector4ui. + * Used only if @ref Flag::Skinning is set and @cpp jointCount > 4 @ce. + */ + typedef Generic3D::SecondaryJointIds SecondaryJointIds; + + /** + * @brief Secondary weights + * @m_since_latest + * + * @ref shaders-generic "Generic attribute", @ref Magnum::Vector4. + * Used only if @ref Flag::Skinning is set and @cpp jointCount > 4 @ce. + */ + typedef Generic3D::Weights SecondaryWeights; + #ifndef MAGNUM_TARGET_GLES2 /** * @brief (Instanced) object ID @@ -416,7 +452,14 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { * in WebGL 1.0. * @m_since_latest */ - InstancedTextureOffset = (1 << 10)|TextureTransformation + InstancedTextureOffset = (1 << 10)|TextureTransformation, + + /** + * Skinning. + * @TODO docs + * @m_since_latest + */ + Skinning = 1 << 11 }; /** @@ -428,10 +471,13 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { /** * @brief Constructor - * @param flags Flags - * @param lightCount Count of light sources + * @param flags Flags + * @param lightCount Count of light sources + * @param jointCount Count of joints for skinning (see @ref Flag::Skinning) + * @param jointsPerVertex Max count of joints that may influence a vertex + * default @cpp 4 @ce (see @ref Flag::Skinning) */ - explicit Phong(Flags flags = {}, UnsignedInt lightCount = 1); + explicit Phong(Flags flags = {}, UnsignedInt lightCount = 1, UnsignedInt jointCount = 0, UnsignedInt jointsPerVertex = 4); /** * @brief Construct without creating the underlying OpenGL object @@ -465,6 +511,12 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { /** @brief Light count */ UnsignedInt lightCount() const { return _lightCount; } + /** @brief Joint count */ + UnsignedInt jointCount() const { return _jointCount; } + + /** @brief Joints per vertex */ + UnsignedInt jointsPerVertex() const { return _jointsPerVertex; } + /** * @brief Set ambient color * @return Reference to self (for method chaining) @@ -719,6 +771,28 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { return setLightColors({&color, 1}); } + /** + * @brief Set joint matrices + * @return Reference to self (for method chaining) + * + * Initial values are identity transformations. Expects that the size + * of the @p matrices array is the same as @ref jointCount(). + * @see @ref setJointMatrix(UnsignedInt, const Matrix4&) + */ + Phong& setJointMatrices(const Containers::ArrayView matrices); + + /** @overload */ + Phong& setJointMatrices(std::initializer_list matrices); + + /** + * @brief Set joint matrix for given joint + * @return Reference to self (for method chaining) + * + * Unlike @ref setJointMatrices() updates just a single joint matrix. + * Expects that @p id is less than @ref joinCount(). + */ + Phong& setJointMatrix(UnsignedInt id, const Matrix4& matrix); + private: /* Prevent accidentally calling irrelevant functions */ #ifndef MAGNUM_TARGET_GLES @@ -730,6 +804,8 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { Flags _flags; UnsignedInt _lightCount; + UnsignedInt _jointCount; + UnsignedInt _jointsPerVertex; Int _transformationMatrixUniform{0}, _projectionMatrixUniform{1}, _normalMatrixUniform{2}, @@ -744,6 +820,7 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { #endif Int _lightPositionsUniform{10}, _lightColorsUniform; /* 10 + lightCount, set in the constructor */ + Int _jointMatricesUniform; /* 10 + 2*lightCount, set in the constructor */ }; /** @debugoperatorclassenum{Phong,Phong::Flag} */ diff --git a/src/Magnum/Shaders/Phong.vert b/src/Magnum/Shaders/Phong.vert index e13c75176..51dd4a18e 100644 --- a/src/Magnum/Shaders/Phong.vert +++ b/src/Magnum/Shaders/Phong.vert @@ -80,6 +80,13 @@ layout(location = 10) uniform highp vec3 lightPositions[LIGHT_COUNT]; /* defaults to zero */ #endif +#ifdef SKINNING +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = JOINT_MATRICES_LOCATION) +#endif +uniform mat4 jointMatrices[JOINT_COUNT]; +#endif + #ifdef EXPLICIT_ATTRIB_LOCATION layout(location = POSITION_ATTRIBUTE_LOCATION) #endif @@ -117,6 +124,30 @@ in lowp vec4 vertexColor; out lowp vec4 interpolatedVertexColor; #endif +#ifdef SKINNING +#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; + +#if JOINTS_PER_VERTEX > 4 +#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 vec4 secondaryJointIds; +#endif +#endif + #ifdef INSTANCED_OBJECT_ID #ifdef EXPLICIT_ATTRIB_LOCATION layout(location = OBJECT_ID_ATTRIBUTE_LOCATION) @@ -155,11 +186,25 @@ out highp vec3 cameraDirection; #endif void main() { + #ifdef SKINNING + mat4 skinMatrix; + int i = 0; + for(; i != JOINTS_PER_VERTEX && i != 4; ++i) + skinMatrix += weights[i]*jointMatrices[int(jointIds[i])]; + #if JOINTS_PER_VERTEX > 4 + for(i = 0; i != JOINTS_PER_VERTEX - 4 && i != 4; ++i) + skinMatrix += secondaryWeights[i]*jointMatrices[int(secondaryJointIds[i])]; + #endif + #endif + /* Transformed vertex position */ highp vec4 transformedPosition4 = transformationMatrix* #ifdef INSTANCED_TRANSFORMATION instancedTransformationMatrix* #endif + #ifdef SKINNING + skinMatrix* + #endif position; highp vec3 transformedPosition = transformedPosition4.xyz/transformedPosition4.w; diff --git a/src/Magnum/Shaders/generic.glsl b/src/Magnum/Shaders/generic.glsl index 180bc4d22..0460d3088 100644 --- a/src/Magnum/Shaders/generic.glsl +++ b/src/Magnum/Shaders/generic.glsl @@ -37,6 +37,11 @@ #define NORMAL_MATRIX_ATTRIBUTE_LOCATION 12 #define TEXTURE_OFFSET_ATTRIBUTE_LOCATION 15 +#define WEIGHTS_ATTRIBUTE_LOCATION 6 +#define JOINTIDS_ATTRIBUTE_LOCATION 7 +#define SECONDARY_WEIGHTS_ATTRIBUTE_LOCATION 10 +#define SECONDARY_JOINTIDS_ATTRIBUTE_LOCATION 11 + /* Outputs */ #define COLOR_OUTPUT_ATTRIBUTE_LOCATION 0 #define OBJECT_ID_OUTPUT_ATTRIBUTE_LOCATION 1