GLES: Added IQM bone support.

This commit is contained in:
Emile Belanger 2022-11-13 20:06:45 +00:00 committed by Christoph Oelckers
parent 7517b64aee
commit f41f393e91
11 changed files with 165 additions and 48 deletions

View file

@ -275,8 +275,10 @@ void GLBuffer::GPUWaitSync()
void GLVertexBuffer::SetFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs)
{
static int VFmtToGLFmt[] = { GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_UNSIGNED_BYTE, GL_FLOAT }; // TODO Fix last entry GL_INT_2_10_10_10_REV, normals for models will be broken
static uint8_t VFmtToSize[] = {4, 3, 2, 1, 4, 4};
static int VFmtToGLFmt[] = { GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_FLOAT, GL_UNSIGNED_BYTE, GL_INT_2_10_10_10_REV, GL_UNSIGNED_BYTE }; // TODO Fix last entry GL_INT_2_10_10_10_REV, normals for models will be broken
static uint8_t VFmtToSize[] = {4, 3, 2, 1, 4, 4, 4};
static bool VFmtToNormalize[] = { false, false, false, false, true, true, false };
static bool VFmtToIntegerType[] = { false, false, false, false, false, false, true };
mStride = stride;
mNumBindingPoints = numBindingPoints;
@ -290,6 +292,8 @@ void GLVertexBuffer::SetFormat(int numBindingPoints, int numAttributes, size_t s
attrinf.size = VFmtToSize[attrs[i].format];
attrinf.offset = attrs[i].offset;
attrinf.bindingpoint = attrs[i].binding;
attrinf.normalize = VFmtToNormalize[attrs[i].format];
attrinf.integerType = VFmtToIntegerType[attrs[i].format];
}
}
}
@ -310,7 +314,13 @@ void GLVertexBuffer::Bind(int *offsets)
{
glEnableVertexAttribArray(i);
size_t ofs = offsets == nullptr ? attrinf.offset : attrinf.offset + mStride * offsets[attrinf.bindingpoint];
glVertexAttribPointer(i, attrinf.size, attrinf.format, attrinf.format != GL_FLOAT, (GLsizei)mStride, (void*)(intptr_t)ofs);
if (!attrinf.integerType)
glVertexAttribPointer(i, attrinf.size, attrinf.format, attrinf.normalize, (GLsizei)mStride, (void*)(intptr_t)ofs);
else
{
if (gles.gles3Features)
glVertexAttribIPointer(i, attrinf.size, attrinf.format, (GLsizei)mStride, (void*)(intptr_t)ofs);
}
}
i++;
}

View file

@ -49,6 +49,8 @@ class GLVertexBuffer : public IVertexBuffer, public GLBuffer
{
int bindingpoint;
int format;
bool normalize;
bool integerType;
int size;
int offset;
};

View file

@ -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"
@ -110,15 +111,13 @@ bool FGLRenderState::ApplyShader()
{
lightPtr = ((float*)screen->mLights->GetBuffer()->Memory());
lightPtr += ((int64_t)mLightIndex * 4);
//float array[64];
//memcpy(array, ptr, 4 * 64);
// Calculate how much light data there is to upload, this is stored in the first 4 floats
modLights = int(lightPtr[1]) / LIGHT_VEC4_NUM;
subLights = (int(lightPtr[2]) - int(lightPtr[1])) / LIGHT_VEC4_NUM;
addLights = (int(lightPtr[3]) - int(lightPtr[2])) / LIGHT_VEC4_NUM;
// Here we limit the number of lights, but dont' change the light data so priority has to be mod, sub then add
// Here we limit the number of lights, but don't change the light data so priority has to be mod, sub then add
if (modLights > (int)gles.maxlights)
modLights = gles.maxlights;
@ -224,12 +223,9 @@ bool FGLRenderState::ApplyShader()
activeShader->cur->muProjectionMatrix.Set(&mHwUniforms->mProjectionMatrix);
activeShader->cur->muViewMatrix.Set(&mHwUniforms->mViewMatrix);
activeShader->cur->muNormalViewMatrix.Set(&mHwUniforms->mNormalViewMatrix);
activeShader->cur->muCameraPos.Set(&mHwUniforms->mCameraPos.X);
activeShader->cur->muClipLine.Set(&mHwUniforms->mClipLine.X);
activeShader->cur->muGlobVis.Set(mHwUniforms->mGlobVis);
activeShader->cur->muPalLightLevels.Set(mHwUniforms->mPalLightLevels & 0xFF); // JUST pass the pal levels, clear the top bits
activeShader->cur->muViewHeight.Set(mHwUniforms->mViewHeight);
activeShader->cur->muClipHeight.Set(mHwUniforms->mClipHeight);
@ -250,6 +246,7 @@ bool FGLRenderState::ApplyShader()
activeShader->cur->muInterpolationFactor.Set(mStreamData.uInterpolationFactor);
activeShader->cur->muTimer.Set((double)(screen->FrameTime - firstFrame) * (double)mShaderTimer / 1000.);
activeShader->cur->muAlphaThreshold.Set(mAlphaThreshold);
activeShader->cur->muBoneIndexBase.Set(-1);
activeShader->cur->muClipSplit.Set(mClipSplit);
activeShader->cur->muSpecularMaterial.Set(mGlossiness, mSpecularLevel);
activeShader->cur->muAddColor.Set(mStreamData.uAddColor);
@ -349,6 +346,23 @@ bool FGLRenderState::ApplyShader()
activeShader->cur->muLightRange.Set(range);
}
if (gles.gles3Features)
{
// Upload bone data
// NOTE, this is pretty inefficient, it will be reloading the same data over and over in a single frame
// Need to add something to detect a start of new frame then only update the data when it's been changed
if ((mBoneIndexBase >= 0))
{
float* bonesPtr = ((float*)screen->mBones->GetBuffer()->Memory());
int number = screen->mBones->GetCurrentIndex();
glUniformMatrix4fv(activeShader->cur->bones_index, number, false, bonesPtr);
activeShader->cur->muBoneIndexBase.Set(mBoneIndexBase);
}
}
return true;
}
@ -369,18 +383,6 @@ void FGLRenderState::ApplyState()
if (mSplitEnabled != stSplitEnabled)
{
/*
if (mSplitEnabled)
{
glEnable(GL_CLIP_DISTANCE3);
glEnable(GL_CLIP_DISTANCE4);
}
else
{
glDisable(GL_CLIP_DISTANCE3);
glDisable(GL_CLIP_DISTANCE4);
}
*/
stSplitEnabled = mSplitEnabled;
}

View file

@ -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
@ -108,16 +109,7 @@ public:
void EnableDrawBuffers(int count, bool apply = false) override
{
/*
count = min(count, 3);
if (mNumDrawBuffers != count)
{
static GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
glDrawBuffers(count, buffers);
mNumDrawBuffers = count;
}
if (apply) Apply();
*/
}
void ToggleState(int state, bool on);

View file

@ -267,6 +267,9 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
// light buffers
uniform vec4 lights[MAXIMUM_LIGHT_VECTORS];
// bone matrix buffers
uniform mat4 bones[MAXIMUM_LIGHT_VECTORS];
uniform mat4 ProjectionMatrix;
uniform mat4 ViewMatrix;
uniform mat4 NormalViewMatrix;
@ -321,6 +324,9 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
// dynamic lights
uniform ivec4 uLightRange;
// bone animation
uniform int uBoneIndexBase;
// Blinn glossiness and specular level
uniform vec2 uSpecularMaterial;
@ -391,10 +397,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 %s\n\n#define NO_CLIPDISTANCE_SUPPORT\n", gles.shaderVersionString);
FString fp_comb = vp_comb;
vp_comb << defines << i_data.GetChars();
@ -529,6 +536,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);
@ -569,10 +578,6 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
shaderData->muViewMatrix.Init(shaderData->hShader, "ViewMatrix");
shaderData->muNormalViewMatrix.Init(shaderData->hShader, "NormalViewMatrix");
//shaderData->ProjectionMatrix_index = glGetUniformLocation(shaderData->hShader, "ProjectionMatrix");
//shaderData->ViewMatrix_index = glGetUniformLocation(shaderData->hShader, "ViewMatrix");
//shaderData->NormalViewMatrix_index = glGetUniformLocation(shaderData->hShader, "NormalViewMatrix");
shaderData->muCameraPos.Init(shaderData->hShader, "uCameraPos");
shaderData->muClipLine.Init(shaderData->hShader, "uClipLine");
@ -591,6 +596,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");
@ -619,6 +625,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump_, const char *
shaderData->muFixedColormapRange.Init(shaderData->hShader, "uFixedColormapRange");
shaderData->lights_index = glGetUniformLocation(shaderData->hShader, "lights");
shaderData->bones_index = glGetUniformLocation(shaderData->hShader, "bones");
shaderData->modelmatrix_index = glGetUniformLocation(shaderData->hShader, "ModelMatrix");
shaderData->texturematrix_index = glGetUniformLocation(shaderData->hShader, "TextureMatrix");
shaderData->normalmodelmatrix_index = glGetUniformLocation(shaderData->hShader, "NormalModelMatrix");

View file

@ -326,6 +326,7 @@ public: class ShaderVariantData
FBufferedUniform1i muTextureMode;
FBufferedUniform4f muLightParms;
FBufferedUniform2f muClipSplit;
FBufferedUniform1i muBoneIndexBase;
FBufferedUniform4i muLightRange;
FBufferedUniformPE muFogColor;
FBufferedUniform4f muDynLightColor;
@ -356,6 +357,7 @@ public: class ShaderVariantData
int lights_index = 0;
int bones_index = 0;
int modelmatrix_index = 0;
int normalmodelmatrix_index = 0;
int texturematrix_index = 0;

View file

@ -236,7 +236,7 @@ FString FShaderProgram::PatchShader(ShaderType type, const FString &code, const
{
FString patchedCode;
patchedCode.AppendFormat("#version %d\n", 100); // Set to GLES2
patchedCode.AppendFormat("#version %s\n", gles.shaderVersionString);
patchedCode += GetGLSLPrecision();

View file

@ -16,6 +16,7 @@ void setGlVersion(double glv);
PFNGLMAPBUFFERRANGEEXTPROC glMapBufferRange = NULL;
PFNGLUNMAPBUFFEROESPROC glUnmapBuffer = NULL;
PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer = NULL;
#ifdef __ANDROID__
#include <dlfcn.h>
@ -134,7 +135,7 @@ namespace OpenGLESRenderer
glMapBufferRange = (PFNGLMAPBUFFERRANGEEXTPROC)LoadGLES2Proc("glMapBufferRange");
glUnmapBuffer = (PFNGLUNMAPBUFFEROESPROC)LoadGLES2Proc("glUnmapBuffer");
glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)LoadGLES2Proc("glVertexAttribIPointer");
#else
static bool first = true;
@ -181,11 +182,16 @@ namespace OpenGLESRenderer
Printf("GL_MAX_TEXTURE_SIZE: %d\n", gles.max_texturesize);
#if USE_GLES2
gles.gles3Features = false; // Enales IQM bones
gles.shaderVersionString = "100";
gles.depthStencilAvailable = CheckExtension("GL_OES_packed_depth_stencil");
gles.npotAvailable = CheckExtension("GL_OES_texture_npot");
gles.depthClampAvailable = CheckExtension("GL_EXT_depth_clamp");
gles.anistropicFilterAvailable = CheckExtension("GL_EXT_texture_filter_anisotropic");
#else
gles.gles3Features = true;
gles.shaderVersionString = "330";
gles.depthStencilAvailable = true;
gles.npotAvailable = true;
gles.useMappedBuffers = true;

View file

@ -36,6 +36,9 @@ GLAPI PFNGLMAPBUFFERRANGEEXTPROC glMapBufferRange;
typedef GLboolean(APIENTRYP PFNGLUNMAPBUFFEROESPROC)(GLenum target);
GLAPI PFNGLUNMAPBUFFEROESPROC glUnmapBuffer;
typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void* pointer);
GLAPI PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer;
#define GL_DEPTH24_STENCIL8 0x88F0
#define GL_MAP_PERSISTENT_BIT 0x0040
#define GL_MAP_READ_BIT 0x0001
@ -45,7 +48,7 @@ GLAPI PFNGLUNMAPBUFFEROESPROC glUnmapBuffer;
#define GL_BGRA 0x80E1
#define GL_DEPTH_CLAMP 0x864F
#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
#define GL_INT_2_10_10_10_REV 0x8D9F
#else
#include "gl_load/gl_load.h"
#endif
@ -74,6 +77,8 @@ namespace OpenGLESRenderer
bool forceGLSLv100;
bool depthClampAvailable;
bool anistropicFilterAvailable;
bool gles3Features;
const char* shaderVersionString;
int max_texturesize;
char* vendorstring;
char* modelstring;

View file

@ -35,6 +35,9 @@ public:
bool GetBufferType() const { return mBufferType; }
int GetBinding(unsigned int index, size_t* pOffset, size_t* pSize);
// Only for GLES to determin how much data is in the buffer
int GetCurrentIndex() { return mIndex; };
// OpenGL needs the buffer to mess around with the binding.
IDataBuffer* GetBuffer() const
{

View file

@ -10,6 +10,10 @@ varying vec4 vColor;
attribute vec4 aVertex2;
attribute vec4 aNormal;
attribute vec4 aNormal2;
#if __VERSION__ >= 300
attribute vec4 aBoneWeight;
attribute uvec4 aBoneSelector;
#endif
varying vec4 pixelpos;
varying vec3 glowdist;
@ -23,6 +27,15 @@ varying vec4 ClipDistanceA;
varying vec4 ClipDistanceB;
#endif
struct BonesResult
{
vec3 Normal;
vec4 Position;
};
BonesResult ApplyBones();
void main()
{
float ClipDistance0, ClipDistance1, ClipDistance2, ClipDistance3, ClipDistance4;
@ -30,8 +43,10 @@ void main()
vec2 parmTexCoord;
vec4 parmPosition;
BonesResult bones = ApplyBones();
parmTexCoord = aTexCoord;
parmPosition = aPosition;
parmPosition = bones.Position;
#ifndef SIMPLE
vec4 worldcoord = ModelMatrix * mix(parmPosition, aVertex2, uInterpolationFactor);
@ -77,15 +92,9 @@ void main()
ClipDistance3 = ((uSplitTopPlane.w + uSplitTopPlane.x * worldcoord.x + uSplitTopPlane.y * worldcoord.z) * uSplitTopPlane.z) - worldcoord.y;
ClipDistance4 = worldcoord.y - ((uSplitBottomPlane.w + uSplitBottomPlane.x * worldcoord.x + uSplitBottomPlane.y * worldcoord.z) * uSplitBottomPlane.z);
}
vWorldNormal = NormalModelMatrix * vec4(normalize(bones.Normal), 1.0);
#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
vEyeNormal = NormalViewMatrix * vWorldNormal;
#endif
@ -129,3 +138,82 @@ 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
}
#if __VERSION__ >= 300
void AddWeightedBone(uint boneIndex, float weight, inout vec4 position, inout vec3 normal)
{
if (weight != 0.0)
{
mat4 transform = bones[uBoneIndexBase + int(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(aBoneSelector.x, boneWeight.x, result.Position, result.Normal);
AddWeightedBone(aBoneSelector.y, boneWeight.y, result.Position, result.Normal);
AddWeightedBone(aBoneSelector.z, boneWeight.z, result.Position, result.Normal);
AddWeightedBone(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;
result.Normal = GetAttrNormal();
return result;
}
#endif
#else // SIMPLE
BonesResult ApplyBones()
{
BonesResult result;
result.Position = aPosition;
return result;
}
#endif