diff --git a/src/common/rendering/gles/gles_renderstate.cpp b/src/common/rendering/gles/gles_renderstate.cpp index 76718f6490..910c7ae20e 100644 --- a/src/common/rendering/gles/gles_renderstate.cpp +++ b/src/common/rendering/gles/gles_renderstate.cpp @@ -32,6 +32,7 @@ #include "gles_shader.h" #include "gles_renderer.h" #include "hw_lightbuffer.h" +#include "hw_bonebuffer.h" #include "gles_renderbuffers.h" #include "gles_hwtexture.h" #include "gles_buffers.h" @@ -251,6 +252,7 @@ bool FGLRenderState::ApplyShader() activeShader->cur->muTimer.Set((double)(screen->FrameTime - firstFrame) * (double)mShaderTimer / 1000.); activeShader->cur->muAlphaThreshold.Set(mAlphaThreshold); activeShader->cur->muClipSplit.Set(mClipSplit); + activeShader->cur->muBoneIndexBase.Set(-1); activeShader->cur->muSpecularMaterial.Set(mGlossiness, mSpecularLevel); activeShader->cur->muAddColor.Set(mStreamData.uAddColor); activeShader->cur->muTextureAddColor.Set(mStreamData.uTextureAddColor); @@ -349,6 +351,9 @@ bool FGLRenderState::ApplyShader() activeShader->cur->muLightRange.Set(range); } + int index = mBoneIndexBase; + activeShader->cur->muBoneIndexBase.Set(index); + return true; } diff --git a/src/common/rendering/gles/gles_renderstate.h b/src/common/rendering/gles/gles_renderstate.h index 4dd8516ecc..ed035d0f13 100644 --- a/src/common/rendering/gles/gles_renderstate.h +++ b/src/common/rendering/gles/gles_renderstate.h @@ -66,6 +66,7 @@ class FGLRenderState final : public FRenderState int lastTranslation = 0; int maxBoundMaterial = -1; size_t mLastMappedLightIndex = SIZE_MAX; + size_t mLastMappedBoneIndexBase = SIZE_MAX; IVertexBuffer *mCurrentVertexBuffer; int mCurrentVertexOffsets[2]; // one per binding point diff --git a/src/common/rendering/gles/gles_shader.cpp b/src/common/rendering/gles/gles_shader.cpp index daba34f6d1..bbb97d09c6 100644 --- a/src/common/rendering/gles/gles_shader.cpp +++ b/src/common/rendering/gles/gles_shader.cpp @@ -38,6 +38,7 @@ #include "shaderuniforms.h" #include "hw_viewpointuniforms.h" #include "hw_lightbuffer.h" +#include "hw_bonebuffer.h" #include "i_specialpaths.h" #include "printf.h" #include "version.h" @@ -321,6 +322,12 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char * // dynamic lights uniform ivec4 uLightRange; + // bone animation + uniform int uBoneIndexBase; + + // bone matrix buffers + uniform mat4 bones[NUM_UBO_BONES]; + // Blinn glossiness and specular level uniform vec2 uSpecularMaterial; @@ -391,10 +398,11 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char * FString vp_comb; assert(screen->mLights != NULL); + assert(screen->mBones != NULL); unsigned int lightbuffersize = screen->mLights->GetBlockSize(); - vp_comb.Format("#version 100\n#define NUM_UBO_LIGHTS %d\n#define NO_CLIPDISTANCE_SUPPORT\n", lightbuffersize); + vp_comb.Format("#version 100\n#define NUM_UBO_LIGHTS %d\n#define NO_CLIPDISTANCE_SUPPORT\n#define NUM_UBO_BONES %d\n", lightbuffersize, screen->mBones->GetBlockSize()); FString fp_comb = vp_comb; vp_comb << defines << i_data.GetChars(); @@ -529,6 +537,8 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char * glBindAttribLocation(shaderData->hShader, VATTR_VERTEX2, "aVertex2"); glBindAttribLocation(shaderData->hShader, VATTR_NORMAL, "aNormal"); glBindAttribLocation(shaderData->hShader, VATTR_NORMAL2, "aNormal2"); + glBindAttribLocation(shaderData->hShader, VATTR_BONEWEIGHT, "aBoneWeight"); + glBindAttribLocation(shaderData->hShader, VATTR_BONESELECTOR, "aBoneSelector"); glLinkProgram(shaderData->hShader); @@ -591,6 +601,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char * shaderData->muLightParms.Init(shaderData->hShader, "uLightAttr"); shaderData->muClipSplit.Init(shaderData->hShader, "uClipSplit"); shaderData->muLightRange.Init(shaderData->hShader, "uLightRange"); + shaderData->muBoneIndexBase.Init(shaderData->hShader, "uBoneIndexBase"); shaderData->muFogColor.Init(shaderData->hShader, "uFogColor"); shaderData->muDynLightColor.Init(shaderData->hShader, "uDynLightColor"); shaderData->muObjectColor.Init(shaderData->hShader, "uObjectColor"); diff --git a/src/common/rendering/gles/gles_shader.h b/src/common/rendering/gles/gles_shader.h index 72a242c1ba..217a8600f5 100644 --- a/src/common/rendering/gles/gles_shader.h +++ b/src/common/rendering/gles/gles_shader.h @@ -327,6 +327,7 @@ public: class ShaderVariantData FBufferedUniform4f muLightParms; FBufferedUniform2f muClipSplit; FBufferedUniform4i muLightRange; + FBufferedUniform1i muBoneIndexBase; FBufferedUniformPE muFogColor; FBufferedUniform4f muDynLightColor; FBufferedUniformPE muObjectColor; diff --git a/wadsrc/static/shaders_gles/glsl/main.vp b/wadsrc/static/shaders_gles/glsl/main.vp index 9d78e38a1f..a3f877bc20 100644 --- a/wadsrc/static/shaders_gles/glsl/main.vp +++ b/wadsrc/static/shaders_gles/glsl/main.vp @@ -10,6 +10,8 @@ varying vec4 vColor; attribute vec4 aVertex2; attribute vec4 aNormal; attribute vec4 aNormal2; +attribute vec4 aBoneWeight; +attribute vec4 aBoneSelector; varying vec4 pixelpos; varying vec3 glowdist; @@ -23,15 +25,25 @@ varying vec4 ClipDistanceA; varying vec4 ClipDistanceB; #endif +struct BonesResult +{ + vec3 Normal; + vec4 Position; +}; + +BonesResult ApplyBones(); + void main() { float ClipDistance0, ClipDistance1, ClipDistance2, ClipDistance3, ClipDistance4; vec2 parmTexCoord; vec4 parmPosition; + + BonesResult bones = ApplyBones(); parmTexCoord = aTexCoord; - parmPosition = aPosition; + parmPosition = bones.Position; #ifndef SIMPLE vec4 worldcoord = ModelMatrix * mix(parmPosition, aVertex2, uInterpolationFactor); @@ -78,14 +90,7 @@ void main() ClipDistance4 = worldcoord.y - ((uSplitBottomPlane.w + uSplitBottomPlane.x * worldcoord.x + uSplitBottomPlane.y * worldcoord.z) * uSplitBottomPlane.z); } - #ifdef HAS_UNIFORM_VERTEX_DATA - if ((useVertexData & 2) == 0) - vWorldNormal = NormalModelMatrix * vec4(uVertexNormal.xyz, 1.0); - else - vWorldNormal = NormalModelMatrix * vec4(normalize(mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor)), 1.0); - #else - vWorldNormal = NormalModelMatrix * vec4(normalize(mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor)), 1.0); - #endif + vWorldNormal = NormalModelMatrix * vec4(normalize(bones.Normal), 1.0); vEyeNormal = NormalViewMatrix * vWorldNormal; #endif @@ -129,3 +134,66 @@ void main() gl_Position = ProjectionMatrix * eyeCoordPos; } + +#if !defined(SIMPLE) +vec3 GetAttrNormal() +{ + #ifdef HAS_UNIFORM_VERTEX_DATA + if ((useVertexData & 2) == 0) + return uVertexNormal.xyz; + else + return mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor); + #else + return mix(aNormal.xyz, aNormal2.xyz, uInterpolationFactor); + #endif +} + +void AddWeightedBone(int boneIndex, float weight, inout vec4 position, inout vec3 normal) +{ + if (weight != 0.0) + { + mat4 transform = bones[uBoneIndexBase + boneIndex]; + mat3 rotation = mat3(transform); + position += (transform * aPosition) * weight; + normal += (rotation * aNormal.xyz) * weight; + } +} + +BonesResult ApplyBones() +{ + BonesResult result; + if (uBoneIndexBase >= 0 && aBoneWeight != vec4(0.0)) + { + result.Position = vec4(0.0); + result.Normal = vec3(0.0); + + // We use low precision input for our bone weights. Rescale so the sum still is 1.0 + float totalWeight = aBoneWeight.x + aBoneWeight.y + aBoneWeight.z + aBoneWeight.w; + float weightMultiplier = 1.0 / totalWeight; + vec4 boneWeight = aBoneWeight * weightMultiplier; + + AddWeightedBone(int(aBoneSelector.x), boneWeight.x, result.Position, result.Normal); + AddWeightedBone(int(aBoneSelector.y), boneWeight.y, result.Position, result.Normal); + AddWeightedBone(int(aBoneSelector.z), boneWeight.z, result.Position, result.Normal); + AddWeightedBone(int(aBoneSelector.w), boneWeight.w, result.Position, result.Normal); + + result.Position.w = 1.0; // For numerical stability + } + else + { + result.Position = aPosition; + result.Normal = GetAttrNormal(); + } + return result; +} + +#else + +BonesResult ApplyBones() +{ + BonesResult result; + result.Position = aPosition; + return result; +} + +#endif