raze/source/common/rendering/gles/gles_shader.h
2021-12-30 09:58:46 +01:00

493 lines
11 KiB
C++

//
//---------------------------------------------------------------------------
//
// Copyright(C) 2004-2016 Christoph Oelckers
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
#ifndef __GL_SHADERS_H__
#define __GL_SHADERS_H__
#include <map>
#include "gles_renderstate.h"
#include "name.h"
extern bool gl_shaderactive;
struct HWViewpointUniforms;
namespace OpenGLESRenderer
{
class FShaderCollection;
//==========================================================================
//
//
//==========================================================================
class FUniform1i
{
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
}
void Set(int newvalue)
{
glUniform1i(mIndex, newvalue);
}
};
class FBufferedUniform1i
{
int mBuffer;
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
mBuffer = 0;
}
void Set(int newvalue)
{
if (newvalue != mBuffer)
{
mBuffer = newvalue;
glUniform1i(mIndex, newvalue);
}
}
};
class FBufferedUniform4i
{
int mBuffer[4];
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
memset(mBuffer, 0, sizeof(mBuffer));
}
void Set(const int *newvalue)
{
if (memcmp(newvalue, mBuffer, sizeof(mBuffer)))
{
memcpy(mBuffer, newvalue, sizeof(mBuffer));
glUniform4iv(mIndex, 1, newvalue);
}
}
};
class FBufferedUniform1f
{
float mBuffer;
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
mBuffer = 0;
}
void Set(float newvalue)
{
if (newvalue != mBuffer)
{
mBuffer = newvalue;
glUniform1f(mIndex, newvalue);
}
}
};
class FBufferedUniform2f
{
float mBuffer[2];
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
memset(mBuffer, 0, sizeof(mBuffer));
}
void Set(const float *newvalue)
{
if (memcmp(newvalue, mBuffer, sizeof(mBuffer)))
{
memcpy(mBuffer, newvalue, sizeof(mBuffer));
glUniform2fv(mIndex, 1, newvalue);
}
}
void Set(float f1, float f2)
{
if (mBuffer[0] != f1 || mBuffer[1] != f2)
{
mBuffer[0] = f1;
mBuffer[1] = f2;
glUniform2fv(mIndex, 1, mBuffer);
}
}
};
class FBufferedUniform4f
{
float mBuffer[4];
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
memset(mBuffer, 0, sizeof(mBuffer));
}
void Set(const float *newvalue)
{
if (memcmp(newvalue, mBuffer, sizeof(mBuffer)))
{
memcpy(mBuffer, newvalue, sizeof(mBuffer));
glUniform4fv(mIndex, 1, newvalue);
}
}
};
class FUniform4f
{
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
}
void Set(const float *newvalue)
{
glUniform4fv(mIndex, 1, newvalue);
}
void Set(float a, float b, float c, float d)
{
glUniform4f(mIndex, a, b, c, d);
}
void Set(PalEntry newvalue)
{
glUniform4f(mIndex, newvalue.r / 255.f, newvalue.g / 255.f, newvalue.b / 255.f, newvalue.a / 255.f);
}
};
class FBufferedUniformPE
{
FVector4PalEntry mBuffer;
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
mBuffer = 0;
}
void Set(const FVector4PalEntry &newvalue)
{
if (newvalue != mBuffer)
{
mBuffer = newvalue;
glUniform4f(mIndex, newvalue.r, newvalue.g, newvalue.b, newvalue.a);
}
}
};
class FBufferedUniformMat4fv
{
VSMatrix mBuffer;
int mIndex;
public:
void Init(GLuint hShader, const GLchar* name)
{
mIndex = glGetUniformLocation(hShader, name);
mBuffer = 0;
}
void Set(const VSMatrix* newvalue)
{
//if (memcmp(newvalue, &mBuffer, sizeof(mBuffer))) // Breaks 2D menu for some reason..
{
mBuffer = *newvalue;
glUniformMatrix4fv(mIndex, 1, false, (float*)newvalue);
}
}
};
class ShaderFlavourData
{
public:
int textureMode;
int texFlags;
int blendFlags;
bool twoDFog;
bool fogEnabled;
bool fogEquationRadial;
bool colouredFog;
bool doDesaturate;
bool dynLightsMod;
bool dynLightsSub;
bool dynLightsAdd;
bool useULightLevel;
bool useObjectColor2;
bool useGlowTopColor;
bool useGlowBottomColor;
bool useColorMap;
bool buildLighting;
bool bandedSwLight;
#ifdef NPOT_EMULATION
bool npotEmulation;
#endif
bool hasSpotLight;
bool paletteInterpolate;
};
class FShader
{
friend class FShaderCollection;
friend class FGLRenderState;
FName mName;
FString mVertProg;
FString mFragProg;
FString mFragProg2;
FString mLightProg;
FString mDefinesBase;
/////
public: class ShaderVariantData
{
public:
unsigned int hShader = 0;
unsigned int hVertProg = 0;
unsigned int hFragProg = 0;
//int ProjectionMatrix_index = 0;
//int ViewMatrix_index = 0;
//int NormalViewMatrix_index = 0;
FBufferedUniformMat4fv muProjectionMatrix;
FBufferedUniformMat4fv muViewMatrix;
FBufferedUniformMat4fv muNormalViewMatrix;
FUniform4f muCameraPos;
FUniform4f muClipLine;
FBufferedUniform1f muGlobVis;
FBufferedUniform1i muPalLightLevels;
FBufferedUniform1i muViewHeight;
FBufferedUniform1f muClipHeight;
FBufferedUniform1f muClipHeightDirection;
FBufferedUniform1i muShadowmapFilter;
/////
FBufferedUniform1f muDesaturation;
FBufferedUniform1i muFogEnabled;
FBufferedUniform1i muTextureMode;
FBufferedUniform4f muLightParms;
FBufferedUniform2f muClipSplit;
FBufferedUniform4i muLightRange;
FBufferedUniformPE muFogColor;
FBufferedUniform4f muDynLightColor;
FBufferedUniformPE muObjectColor;
FBufferedUniformPE muObjectColor2;
FBufferedUniformPE muAddColor;
FBufferedUniformPE muTextureBlendColor;
FBufferedUniformPE muTextureModulateColor;
FBufferedUniformPE muTextureAddColor;
FUniform4f muGlowBottomColor;
FUniform4f muGlowTopColor;
FUniform4f muGlowBottomPlane;
FUniform4f muGlowTopPlane;
FUniform4f muGradientBottomPlane;
FUniform4f muGradientTopPlane;
FUniform4f muSplitBottomPlane;
FUniform4f muSplitTopPlane;
FUniform4f muDetailParms;
FBufferedUniform1f muInterpolationFactor;
FBufferedUniform1f muAlphaThreshold;
FBufferedUniform2f muSpecularMaterial;
FBufferedUniform1f muTimer;
#ifdef NPOT_EMULATION
FBufferedUniform2f muNpotEmulation;
#endif
FUniform4f muFixedColormapStart;
FUniform4f muFixedColormapRange;
int lights_index = 0;
int modelmatrix_index = 0;
int normalmodelmatrix_index = 0;
int texturematrix_index = 0;
int currentglowstate = 0;
int currentgradientstate = 0;
int currentsplitstate = 0;
int currentcliplinestate = 0;
int currentfixedcolormap = 0;
bool currentTextureMatrixState = true;// by setting the matrix state to 'true' it is guaranteed to be set the first time the render state gets applied.
bool currentModelMatrixState = true;
unsigned int GetHandle() const { return hShader; }
};
std::map<uint32_t, ShaderVariantData*> variants;
ShaderVariantData* cur = 0;
public:
FShader(const char *name)
: mName(name)
{
}
~FShader();
bool Load(const char * name, const char * vert_prog_lump, const char * fragprog, const char * fragprog2, const char * light_fragprog, const char *defines);
bool Configure(const char* name, const char* vert_prog_lump, const char* fragprog, const char* fragprog2, const char* light_fragprog, const char* defines);
void LoadVariant();
uint32_t CreateShaderTag(ShaderFlavourData &flavour)
{
uint32_t tag = 0;
tag |= (flavour.textureMode & 0x7);
tag |= (flavour.texFlags & 7) << 3;
tag |= (flavour.blendFlags & 7) << 6;
tag |= (flavour.twoDFog & 1) << 7;
tag |= (flavour.fogEnabled & 1) << 8;
tag |= (flavour.fogEquationRadial & 1) << 9;
tag |= (flavour.colouredFog & 1) << 10;
tag |= (flavour.doDesaturate & 1) << 11;
tag |= (flavour.dynLightsMod & 1) << 12;
tag |= (flavour.dynLightsSub & 1) << 13;
tag |= (flavour.dynLightsAdd & 1) << 14;
tag |= (flavour.useULightLevel & 1) << 15;
tag |= (flavour.useObjectColor2 & 1) << 16;
tag |= (flavour.useGlowTopColor & 1) << 17;
tag |= (flavour.useGlowBottomColor & 1) << 18;
tag |= (flavour.useColorMap & 1) << 19;
tag |= (flavour.buildLighting & 1) << 20;
tag |= (flavour.bandedSwLight & 1) << 21;
#ifdef NPOT_EMULATION
tag |= (flavour.npotEmulation & 1) << 22;
#endif
tag |= (flavour.hasSpotLight & 1) << 23;
tag |= (flavour.paletteInterpolate & 1) << 24;
return tag;
}
bool Bind(ShaderFlavourData& flavour);
};
//==========================================================================
//
// The global shader manager
//
//==========================================================================
class FShaderManager
{
public:
FShaderManager();
~FShaderManager();
FShader *BindEffect(int effect, EPassType passType, ShaderFlavourData& flavour);
FShader *Get(unsigned int eff, bool alphateston, EPassType passType);
void SetActiveShader(FShader::ShaderVariantData *sh);
private:
FShader::ShaderVariantData *mActiveShader = nullptr;
TArray<FShaderCollection*> mPassShaders;
friend class FShader;
};
class FShaderCollection
{
TArray<FShader*> mMaterialShaders;
TArray<FShader*> mMaterialShadersNAT;
FShader *mEffectShaders[MAX_EFFECTS];
void Clean();
void CompileShaders(EPassType passType);
public:
FShaderCollection(EPassType passType);
~FShaderCollection();
FShader *Compile(const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType);
int Find(const char *mame);
FShader *BindEffect(int effect, ShaderFlavourData& flavour);
FShader *Get(unsigned int eff, bool alphateston)
{
// indices 0-2 match the warping modes, 3 no texture, the following are custom
if (!alphateston && eff <= 2)
{
return mMaterialShadersNAT[eff]; // Non-alphatest shaders are only created for default, warp1+2 and brightmap. The rest won't get used anyway
}
if (eff < mMaterialShaders.Size())
{
return mMaterialShaders[eff];
}
else // This can happen if we try and active a user shader which is not loaded, so return default shader so it does not crash
{
return mMaterialShaders[0];
}
}
};
}
#endif