mirror of
https://github.com/ZDoom/Raze.git
synced 2024-12-27 04:30:49 +00:00
49e0c461c2
This is mainly bringing the GLES backend up to date.
955 lines
29 KiB
C++
955 lines
29 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/
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
/*
|
|
** gl_shader.cpp
|
|
**
|
|
** GLSL shader handling
|
|
**
|
|
*/
|
|
|
|
#include "gles_system.h"
|
|
#include "c_cvars.h"
|
|
#include "v_video.h"
|
|
#include "filesystem.h"
|
|
#include "engineerrors.h"
|
|
#include "cmdlib.h"
|
|
#include "md5.h"
|
|
#include "gles_shader.h"
|
|
#include "hw_shaderpatcher.h"
|
|
#include "shaderuniforms.h"
|
|
#include "hw_viewpointuniforms.h"
|
|
#include "hw_lightbuffer.h"
|
|
#include "i_specialpaths.h"
|
|
#include "printf.h"
|
|
#include "version.h"
|
|
|
|
#include "matrix.h"
|
|
#include "gles_renderer.h"
|
|
#include <map>
|
|
#include <memory>
|
|
|
|
namespace OpenGLESRenderer
|
|
{
|
|
|
|
CVAR(Int, gles_glsl_precision, 2, 0); // 0 = low, 1 = medium, 2 = high
|
|
|
|
FString GetGLSLPrecision()
|
|
{
|
|
FString str = "precision highp int;\n \
|
|
precision highp float;\n";
|
|
|
|
if (gles_glsl_precision == 0)
|
|
str.Substitute("highp", "lowp");
|
|
else if (gles_glsl_precision == 1)
|
|
str.Substitute("highp", "mediump");
|
|
|
|
return str;
|
|
}
|
|
|
|
struct ProgramBinary
|
|
{
|
|
uint32_t format;
|
|
TArray<uint8_t> data;
|
|
};
|
|
|
|
static const char *ShaderMagic = "ZDSC";
|
|
|
|
static std::map<FString, std::unique_ptr<ProgramBinary>> ShaderCache; // Not a TMap because it doesn't support unique_ptr move semantics
|
|
|
|
bool IsShaderCacheActive()
|
|
{
|
|
static bool active = true;
|
|
static bool firstcall = true;
|
|
|
|
if (firstcall)
|
|
{
|
|
const char *vendor = (const char *)glGetString(GL_VENDOR);
|
|
active = !(strstr(vendor, "Intel") == nullptr);
|
|
firstcall = false;
|
|
}
|
|
return active;
|
|
}
|
|
|
|
static FString CalcProgramBinaryChecksum(const FString &vertex, const FString &fragment)
|
|
{
|
|
const GLubyte *vendor = glGetString(GL_VENDOR);
|
|
const GLubyte *renderer = glGetString(GL_RENDERER);
|
|
const GLubyte *version = glGetString(GL_VERSION);
|
|
|
|
uint8_t digest[16];
|
|
MD5Context md5;
|
|
md5.Update(vendor, (unsigned int)strlen((const char*)vendor));
|
|
md5.Update(renderer, (unsigned int)strlen((const char*)renderer));
|
|
md5.Update(version, (unsigned int)strlen((const char*)version));
|
|
md5.Update((const uint8_t *)vertex.GetChars(), (unsigned int)vertex.Len());
|
|
md5.Update((const uint8_t *)fragment.GetChars(), (unsigned int)fragment.Len());
|
|
md5.Final(digest);
|
|
|
|
char hexdigest[33];
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
int v = digest[i] >> 4;
|
|
hexdigest[i * 2] = v < 10 ? ('0' + v) : ('a' + v - 10);
|
|
v = digest[i] & 15;
|
|
hexdigest[i * 2 + 1] = v < 10 ? ('0' + v) : ('a' + v - 10);
|
|
}
|
|
hexdigest[32] = 0;
|
|
return hexdigest;
|
|
}
|
|
|
|
static FString CreateProgramCacheName(bool create)
|
|
{
|
|
FString path = M_GetCachePath(create);
|
|
if (create) CreatePath(path);
|
|
path << "/shadercache.zdsc";
|
|
return path;
|
|
}
|
|
|
|
static void LoadShaders()
|
|
{
|
|
static bool loaded = false;
|
|
if (loaded)
|
|
return;
|
|
loaded = true;
|
|
|
|
try
|
|
{
|
|
FString path = CreateProgramCacheName(false);
|
|
FileReader fr;
|
|
if (!fr.OpenFile(path))
|
|
I_Error("Could not open shader file");
|
|
|
|
char magic[4];
|
|
fr.Read(magic, 4);
|
|
if (memcmp(magic, ShaderMagic, 4) != 0)
|
|
I_Error("Not a shader cache file");
|
|
|
|
uint32_t count = fr.ReadUInt32();
|
|
if (count > 512)
|
|
I_Error("Too many shaders cached");
|
|
|
|
for (uint32_t i = 0; i < count; i++)
|
|
{
|
|
char hexdigest[33];
|
|
if (fr.Read(hexdigest, 32) != 32)
|
|
I_Error("Read error");
|
|
hexdigest[32] = 0;
|
|
|
|
std::unique_ptr<ProgramBinary> binary(new ProgramBinary());
|
|
binary->format = fr.ReadUInt32();
|
|
uint32_t size = fr.ReadUInt32();
|
|
if (size > 1024 * 1024)
|
|
I_Error("Shader too big, probably file corruption");
|
|
|
|
binary->data.Resize(size);
|
|
if (fr.Read(binary->data.Data(), binary->data.Size()) != binary->data.Size())
|
|
I_Error("Read error");
|
|
|
|
ShaderCache[hexdigest] = std::move(binary);
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
ShaderCache.clear();
|
|
}
|
|
}
|
|
|
|
static void SaveShaders()
|
|
{
|
|
FString path = CreateProgramCacheName(true);
|
|
std::unique_ptr<FileWriter> fw(FileWriter::Open(path));
|
|
if (fw)
|
|
{
|
|
uint32_t count = (uint32_t)ShaderCache.size();
|
|
fw->Write(ShaderMagic, 4);
|
|
fw->Write(&count, sizeof(uint32_t));
|
|
for (const auto &it : ShaderCache)
|
|
{
|
|
uint32_t size = it.second->data.Size();
|
|
fw->Write(it.first.GetChars(), 32);
|
|
fw->Write(&it.second->format, sizeof(uint32_t));
|
|
fw->Write(&size, sizeof(uint32_t));
|
|
fw->Write(it.second->data.Data(), it.second->data.Size());
|
|
}
|
|
}
|
|
}
|
|
|
|
TArray<uint8_t> LoadCachedProgramBinary(const FString &vertex, const FString &fragment, uint32_t &binaryFormat)
|
|
{
|
|
LoadShaders();
|
|
|
|
auto it = ShaderCache.find(CalcProgramBinaryChecksum(vertex, fragment));
|
|
if (it != ShaderCache.end())
|
|
{
|
|
binaryFormat = it->second->format;
|
|
return it->second->data;
|
|
}
|
|
else
|
|
{
|
|
binaryFormat = 0;
|
|
return {};
|
|
}
|
|
}
|
|
|
|
void SaveCachedProgramBinary(const FString &vertex, const FString &fragment, const TArray<uint8_t> &binary, uint32_t binaryFormat)
|
|
{
|
|
auto &entry = ShaderCache[CalcProgramBinaryChecksum(vertex, fragment)];
|
|
entry.reset(new ProgramBinary());
|
|
entry->format = binaryFormat;
|
|
entry->data = binary;
|
|
|
|
SaveShaders();
|
|
}
|
|
|
|
bool FShader::Configure(const char* name, const char* vert_prog_lump, const char* fragprog, const char* fragprog2, const char* light_fragprog, const char* defines)
|
|
{
|
|
mVertProg = vert_prog_lump;
|
|
mFragProg = fragprog;
|
|
mFragProg2 = fragprog2;
|
|
mLightProg = light_fragprog;
|
|
mDefinesBase = defines;
|
|
|
|
return true;
|
|
}
|
|
|
|
void FShader::LoadVariant()
|
|
{
|
|
//mDefinesBase
|
|
Load(mName.GetChars(), mVertProg, mFragProg, mFragProg2, mLightProg, mDefinesBase);
|
|
}
|
|
|
|
bool FShader::Load(const char * name, const char * vert_prog_lump_, const char * frag_prog_lump_, const char * proc_prog_lump_, const char * light_fragprog_, const char * defines)
|
|
{
|
|
ShaderVariantData* shaderData = new ShaderVariantData();
|
|
|
|
FString vert_prog_lump = vert_prog_lump_;
|
|
FString frag_prog_lump = frag_prog_lump_;
|
|
FString proc_prog_lump = proc_prog_lump_;
|
|
FString light_fragprog = light_fragprog_;
|
|
|
|
vert_prog_lump.Substitute("shaders/", "shaders_gles/");
|
|
frag_prog_lump.Substitute("shaders/", "shaders_gles/");
|
|
proc_prog_lump.Substitute("shaders/", "shaders_gles/");
|
|
light_fragprog.Substitute("shaders/", "shaders_gles/");
|
|
|
|
//light_fragprog.Substitute("material_pbr", "material_normal");
|
|
|
|
if(light_fragprog.Len())
|
|
light_fragprog = "shaders_gles/glsl/material_normal.fp"; // NOTE: Always use normal material for now, ignore others
|
|
|
|
|
|
static char buffer[10000];
|
|
FString error;
|
|
|
|
FString i_data = GetGLSLPrecision();
|
|
|
|
i_data += R"(
|
|
|
|
// light buffers
|
|
uniform vec4 lights[MAXIMUM_LIGHT_VECTORS];
|
|
|
|
uniform mat4 ProjectionMatrix;
|
|
uniform mat4 ViewMatrix;
|
|
uniform mat4 NormalViewMatrix;
|
|
|
|
uniform vec4 uCameraPos;
|
|
uniform vec4 uClipLine;
|
|
|
|
uniform float uGlobVis; // uGlobVis = R_GetGlobVis(r_visibility) / 32.0
|
|
uniform int uPalLightLevels;
|
|
uniform int uViewHeight; // Software fuzz scaling
|
|
uniform float uClipHeight;
|
|
uniform float uClipHeightDirection;
|
|
uniform int uShadowmapFilter;
|
|
|
|
uniform int uTextureMode;
|
|
uniform vec2 uClipSplit;
|
|
uniform float uAlphaThreshold;
|
|
|
|
// colors
|
|
uniform vec4 uObjectColor;
|
|
uniform vec4 uObjectColor2;
|
|
uniform vec4 uDynLightColor;
|
|
uniform vec4 uAddColor;
|
|
uniform vec4 uTextureBlendColor;
|
|
uniform vec4 uTextureModulateColor;
|
|
uniform vec4 uTextureAddColor;
|
|
uniform vec4 uFogColor;
|
|
uniform float uDesaturationFactor;
|
|
uniform float uInterpolationFactor;
|
|
|
|
// Glowing walls stuff
|
|
uniform vec4 uGlowTopPlane;
|
|
uniform vec4 uGlowTopColor;
|
|
uniform vec4 uGlowBottomPlane;
|
|
uniform vec4 uGlowBottomColor;
|
|
|
|
uniform vec4 uGradientTopPlane;
|
|
uniform vec4 uGradientBottomPlane;
|
|
|
|
uniform vec4 uSplitTopPlane;
|
|
uniform vec4 uSplitBottomPlane;
|
|
|
|
uniform vec4 uDetailParms;
|
|
// Lighting + Fog
|
|
uniform vec4 uLightAttr;
|
|
#define uLightLevel uLightAttr.a
|
|
#define uFogDensity uLightAttr.b
|
|
#define uLightFactor uLightAttr.g
|
|
#define uLightDist uLightAttr.r
|
|
//uniform int uFogEnabled;
|
|
|
|
// dynamic lights
|
|
uniform ivec4 uLightRange;
|
|
|
|
// Blinn glossiness and specular level
|
|
uniform vec2 uSpecularMaterial;
|
|
|
|
// matrices
|
|
uniform mat4 ModelMatrix;
|
|
uniform mat4 NormalModelMatrix;
|
|
uniform mat4 TextureMatrix;
|
|
|
|
uniform vec4 uFixedColormapStart;
|
|
uniform vec4 uFixedColormapRange;
|
|
|
|
// textures
|
|
uniform sampler2D tex;
|
|
uniform sampler2D ShadowMap;
|
|
uniform sampler2D texture2;
|
|
uniform sampler2D texture3;
|
|
uniform sampler2D texture4;
|
|
uniform sampler2D texture5;
|
|
uniform sampler2D texture6;
|
|
uniform sampler2D texture7;
|
|
uniform sampler2D texture8;
|
|
uniform sampler2D texture9;
|
|
uniform sampler2D texture10;
|
|
uniform sampler2D texture11;
|
|
|
|
// timer data
|
|
uniform float timer;
|
|
|
|
// material types
|
|
#if defined(SPECULAR)
|
|
#define normaltexture texture2
|
|
#define speculartexture texture3
|
|
#define brighttexture texture4
|
|
#define detailtexture texture5
|
|
#define glowtexture texture6
|
|
#elif defined(PBR)
|
|
#define normaltexture texture2
|
|
#define metallictexture texture3
|
|
#define roughnesstexture texture4
|
|
#define aotexture texture5
|
|
#define brighttexture texture6
|
|
#define detailtexture texture7
|
|
#define glowtexture texture8
|
|
#else
|
|
#define brighttexture texture2
|
|
#define detailtexture texture3
|
|
#define glowtexture texture4
|
|
#endif
|
|
)";
|
|
|
|
#ifdef NPOT_EMULATION
|
|
i_data += "#define NPOT_EMULATION\nuniform vec2 uNpotEmulation;\n";
|
|
#endif
|
|
|
|
int vp_lump = fileSystem.CheckNumForFullName(vert_prog_lump, 0);
|
|
if (vp_lump == -1) I_Error("Unable to load '%s'", vert_prog_lump.GetChars());
|
|
FileData vp_data = fileSystem.ReadFile(vp_lump);
|
|
|
|
int fp_lump = fileSystem.CheckNumForFullName(frag_prog_lump, 0);
|
|
if (fp_lump == -1) I_Error("Unable to load '%s'", frag_prog_lump.GetChars());
|
|
FileData fp_data = fileSystem.ReadFile(fp_lump);
|
|
|
|
|
|
|
|
//
|
|
// The following code uses GetChars on the strings to get rid of terminating 0 characters. Do not remove or the code may break!
|
|
//
|
|
FString vp_comb;
|
|
|
|
assert(screen->mLights != NULL);
|
|
|
|
bool lightbuffertype = screen->mLights->GetBufferType();
|
|
unsigned int lightbuffersize = screen->mLights->GetBlockSize();
|
|
|
|
vp_comb.Format("#version 100\n#define NUM_UBO_LIGHTS %d\n#define NO_CLIPDISTANCE_SUPPORT\n", lightbuffersize);
|
|
|
|
FString fp_comb = vp_comb;
|
|
vp_comb << defines << i_data.GetChars();
|
|
fp_comb << "$placeholder$\n" << defines << i_data.GetChars();
|
|
|
|
vp_comb << "#line 1\n";
|
|
fp_comb << "#line 1\n";
|
|
|
|
vp_comb << RemoveLayoutLocationDecl(vp_data.GetString(), "out").GetChars() << "\n";
|
|
fp_comb << RemoveLayoutLocationDecl(fp_data.GetString(), "in").GetChars() << "\n";
|
|
FString placeholder = "\n";
|
|
|
|
if (proc_prog_lump.Len())
|
|
{
|
|
fp_comb << "#line 1\n";
|
|
|
|
if (*proc_prog_lump != '#')
|
|
{
|
|
int pp_lump = fileSystem.CheckNumForFullName(proc_prog_lump);
|
|
if (pp_lump == -1) I_Error("Unable to load '%s'", proc_prog_lump.GetChars());
|
|
FileData pp_data = fileSystem.ReadFile(pp_lump);
|
|
|
|
if (pp_data.GetString().IndexOf("ProcessMaterial") < 0 && pp_data.GetString().IndexOf("SetupMaterial") < 0)
|
|
{
|
|
// this looks like an old custom hardware shader.
|
|
|
|
if (pp_data.GetString().IndexOf("GetTexCoord") >= 0)
|
|
{
|
|
int pl_lump = fileSystem.CheckNumForFullName("shaders_gles/glsl/func_defaultmat2.fp", 0);
|
|
if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders_gles/glsl/func_defaultmat2.fp");
|
|
FileData pl_data = fileSystem.ReadFile(pl_lump);
|
|
fp_comb << "\n" << pl_data.GetString().GetChars();
|
|
}
|
|
else
|
|
{
|
|
int pl_lump = fileSystem.CheckNumForFullName("shaders_gles/glsl/func_defaultmat.fp", 0);
|
|
if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders_gles/glsl/func_defaultmat.fp");
|
|
FileData pl_data = fileSystem.ReadFile(pl_lump);
|
|
fp_comb << "\n" << pl_data.GetString().GetChars();
|
|
|
|
if (pp_data.GetString().IndexOf("ProcessTexel") < 0)
|
|
{
|
|
// this looks like an even older custom hardware shader.
|
|
// We need to replace the ProcessTexel call to make it work.
|
|
|
|
fp_comb.Substitute("material.Base = ProcessTexel();", "material.Base = Process(vec4(1.0));");
|
|
}
|
|
}
|
|
|
|
if (pp_data.GetString().IndexOf("ProcessLight") >= 0)
|
|
{
|
|
// The ProcessLight signatured changed. Forward to the old one.
|
|
fp_comb << "\nvec4 ProcessLight(vec4 color);\n";
|
|
fp_comb << "\nvec4 ProcessLight(Material material, vec4 color) { return ProcessLight(color); }\n";
|
|
}
|
|
}
|
|
|
|
fp_comb << RemoveLegacyUserUniforms(pp_data.GetString()).GetChars();
|
|
fp_comb.Substitute("gl_TexCoord[0]", "vTexCoord"); // fix old custom shaders.
|
|
|
|
if (pp_data.GetString().IndexOf("ProcessLight") < 0)
|
|
{
|
|
int pl_lump = fileSystem.CheckNumForFullName("shaders_gles/glsl/func_defaultlight.fp", 0);
|
|
if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders_gles/glsl/func_defaultlight.fp");
|
|
FileData pl_data = fileSystem.ReadFile(pl_lump);
|
|
fp_comb << "\n" << pl_data.GetString().GetChars();
|
|
}
|
|
|
|
// ProcessMaterial must be considered broken because it requires the user to fill in data they possibly cannot know all about.
|
|
if (pp_data.GetString().IndexOf("ProcessMaterial") >= 0 && pp_data.GetString().IndexOf("SetupMaterial") < 0)
|
|
{
|
|
// This reactivates the old logic and disables all features that cannot be supported with that method.
|
|
placeholder << "#define LEGACY_USER_SHADER\n";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Proc_prog_lump is not a lump name but the source itself (from generated shaders)
|
|
fp_comb << proc_prog_lump.GetChars() + 1;
|
|
}
|
|
}
|
|
fp_comb.Substitute("$placeholder$", placeholder);
|
|
|
|
if (light_fragprog.Len())
|
|
{
|
|
int pp_lump = fileSystem.CheckNumForFullName(light_fragprog, 0);
|
|
if (pp_lump == -1) I_Error("Unable to load '%s'", light_fragprog.GetChars());
|
|
FileData pp_data = fileSystem.ReadFile(pp_lump);
|
|
fp_comb << pp_data.GetString().GetChars() << "\n";
|
|
}
|
|
|
|
if (gles.flags & RFL_NO_CLIP_PLANES)
|
|
{
|
|
// On ATI's GL3 drivers we have to disable gl_ClipDistance because it's hopelessly broken.
|
|
// This will cause some glitches and regressions but is the only way to avoid total display garbage.
|
|
vp_comb.Substitute("gl_ClipDistance", "//");
|
|
}
|
|
|
|
shaderData->hShader = glCreateProgram();
|
|
|
|
uint32_t binaryFormat = 0;
|
|
TArray<uint8_t> binary;
|
|
if (IsShaderCacheActive())
|
|
binary = LoadCachedProgramBinary(vp_comb, fp_comb, binaryFormat);
|
|
|
|
bool linked = false;
|
|
|
|
if (!linked)
|
|
{
|
|
shaderData->hVertProg = glCreateShader(GL_VERTEX_SHADER);
|
|
shaderData->hFragProg = glCreateShader(GL_FRAGMENT_SHADER);
|
|
|
|
int vp_size = (int)vp_comb.Len();
|
|
int fp_size = (int)fp_comb.Len();
|
|
|
|
const char *vp_ptr = vp_comb.GetChars();
|
|
const char *fp_ptr = fp_comb.GetChars();
|
|
|
|
glShaderSource(shaderData->hVertProg, 1, &vp_ptr, &vp_size);
|
|
glShaderSource(shaderData->hFragProg, 1, &fp_ptr, &fp_size);
|
|
|
|
glCompileShader(shaderData->hVertProg);
|
|
glCompileShader(shaderData->hFragProg);
|
|
|
|
glAttachShader(shaderData->hShader, shaderData->hVertProg);
|
|
glAttachShader(shaderData->hShader, shaderData->hFragProg);
|
|
|
|
|
|
glBindAttribLocation(shaderData->hShader, VATTR_VERTEX, "aPosition");
|
|
glBindAttribLocation(shaderData->hShader, VATTR_TEXCOORD, "aTexCoord");
|
|
glBindAttribLocation(shaderData->hShader, VATTR_COLOR, "aColor");
|
|
glBindAttribLocation(shaderData->hShader, VATTR_VERTEX2, "aVertex2");
|
|
glBindAttribLocation(shaderData->hShader, VATTR_NORMAL, "aNormal");
|
|
glBindAttribLocation(shaderData->hShader, VATTR_NORMAL2, "aNormal2");
|
|
|
|
|
|
glLinkProgram(shaderData->hShader);
|
|
|
|
glGetShaderInfoLog(shaderData->hVertProg, 10000, NULL, buffer);
|
|
if (*buffer)
|
|
{
|
|
error << "Vertex shader:\n" << buffer << "\n";
|
|
}
|
|
glGetShaderInfoLog(shaderData->hFragProg, 10000, NULL, buffer);
|
|
if (*buffer)
|
|
{
|
|
error << "Fragment shader:\n" << buffer << "\n";
|
|
}
|
|
|
|
glGetProgramInfoLog(shaderData->hShader, 10000, NULL, buffer);
|
|
if (*buffer)
|
|
{
|
|
error << "Linking:\n" << buffer << "\n";
|
|
}
|
|
GLint status = 0;
|
|
glGetProgramiv(shaderData->hShader, GL_LINK_STATUS, &status);
|
|
linked = (status == GL_TRUE);
|
|
|
|
if (!linked)
|
|
{
|
|
// only print message if there's an error.
|
|
I_Error("Init Shader '%s':\n%s\n", name, error.GetChars());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
shaderData->hVertProg = 0;
|
|
shaderData->hFragProg = 0;
|
|
}
|
|
|
|
shaderData->muProjectionMatrix.Init(shaderData->hShader, "ProjectionMatrix");
|
|
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");
|
|
|
|
shaderData->muGlobVis.Init(shaderData->hShader, "uGlobVis");
|
|
shaderData->muPalLightLevels.Init(shaderData->hShader, "uPalLightLevels");
|
|
shaderData->muViewHeight.Init(shaderData->hShader, "uViewHeight");
|
|
shaderData->muClipHeight.Init(shaderData->hShader, "uClipHeight");
|
|
shaderData->muClipHeightDirection.Init(shaderData->hShader, "uClipHeightDirection");
|
|
shaderData->muShadowmapFilter.Init(shaderData->hShader, "uShadowmapFilter");
|
|
|
|
////
|
|
|
|
shaderData->muDesaturation.Init(shaderData->hShader, "uDesaturationFactor");
|
|
shaderData->muFogEnabled.Init(shaderData->hShader, "uFogEnabled");
|
|
shaderData->muTextureMode.Init(shaderData->hShader, "uTextureMode");
|
|
shaderData->muLightParms.Init(shaderData->hShader, "uLightAttr");
|
|
shaderData->muClipSplit.Init(shaderData->hShader, "uClipSplit");
|
|
shaderData->muLightRange.Init(shaderData->hShader, "uLightRange");
|
|
shaderData->muFogColor.Init(shaderData->hShader, "uFogColor");
|
|
shaderData->muDynLightColor.Init(shaderData->hShader, "uDynLightColor");
|
|
shaderData->muObjectColor.Init(shaderData->hShader, "uObjectColor");
|
|
shaderData->muObjectColor2.Init(shaderData->hShader, "uObjectColor2");
|
|
shaderData->muGlowBottomColor.Init(shaderData->hShader, "uGlowBottomColor");
|
|
shaderData->muGlowTopColor.Init(shaderData->hShader, "uGlowTopColor");
|
|
shaderData->muGlowBottomPlane.Init(shaderData->hShader, "uGlowBottomPlane");
|
|
shaderData->muGlowTopPlane.Init(shaderData->hShader, "uGlowTopPlane");
|
|
shaderData->muGradientBottomPlane.Init(shaderData->hShader, "uGradientBottomPlane");
|
|
shaderData->muGradientTopPlane.Init(shaderData->hShader, "uGradientTopPlane");
|
|
shaderData->muSplitBottomPlane.Init(shaderData->hShader, "uSplitBottomPlane");
|
|
shaderData->muSplitTopPlane.Init(shaderData->hShader, "uSplitTopPlane");
|
|
shaderData->muDetailParms.Init(shaderData->hShader, "uDetailParms");
|
|
shaderData->muInterpolationFactor.Init(shaderData->hShader, "uInterpolationFactor");
|
|
shaderData->muAlphaThreshold.Init(shaderData->hShader, "uAlphaThreshold");
|
|
shaderData->muSpecularMaterial.Init(shaderData->hShader, "uSpecularMaterial");
|
|
shaderData->muAddColor.Init(shaderData->hShader, "uAddColor");
|
|
shaderData->muTextureAddColor.Init(shaderData->hShader, "uTextureAddColor");
|
|
shaderData->muTextureModulateColor.Init(shaderData->hShader, "uTextureModulateColor");
|
|
shaderData->muTextureBlendColor.Init(shaderData->hShader, "uTextureBlendColor");
|
|
shaderData->muTimer.Init(shaderData->hShader, "timer");
|
|
#ifdef NPOT_EMULATION
|
|
shaderData->muNpotEmulation.Init(shaderData->hShader, "uNpotEmulation");
|
|
#endif
|
|
shaderData->muFixedColormapStart.Init(shaderData->hShader, "uFixedColormapStart");
|
|
shaderData->muFixedColormapRange.Init(shaderData->hShader, "uFixedColormapRange");
|
|
|
|
shaderData->lights_index = glGetUniformLocation(shaderData->hShader, "lights");
|
|
shaderData->modelmatrix_index = glGetUniformLocation(shaderData->hShader, "ModelMatrix");
|
|
shaderData->texturematrix_index = glGetUniformLocation(shaderData->hShader, "TextureMatrix");
|
|
shaderData->normalmodelmatrix_index = glGetUniformLocation(shaderData->hShader, "NormalModelMatrix");
|
|
|
|
|
|
glUseProgram(shaderData->hShader);
|
|
|
|
// set up other texture units (if needed by the shader)
|
|
for (int i = 2; i<16; i++)
|
|
{
|
|
char stringbuf[20];
|
|
mysnprintf(stringbuf, 20, "texture%d", i);
|
|
int tempindex = glGetUniformLocation(shaderData->hShader, stringbuf);
|
|
if (tempindex >= 0) glUniform1i(tempindex, i - 1);
|
|
}
|
|
|
|
int shadowmapindex = glGetUniformLocation(shaderData->hShader, "ShadowMap");
|
|
if (shadowmapindex >= 0) glUniform1i(shadowmapindex, 16);
|
|
|
|
glUseProgram(0);
|
|
|
|
cur = shaderData;
|
|
|
|
return linked;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
FShader::~FShader()
|
|
{
|
|
std::map<uint32_t, ShaderVariantData*>::iterator it = variants.begin();
|
|
|
|
while (it != variants.end())
|
|
{
|
|
glDeleteProgram(it->second->hShader);
|
|
if (it->second->hVertProg != 0)
|
|
glDeleteShader(it->second->hVertProg);
|
|
if (it->second->hFragProg != 0)
|
|
glDeleteShader(it->second->hFragProg);
|
|
it++;
|
|
}
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
bool FShader::Bind(ShaderFlavourData& flavour)
|
|
{
|
|
uint32_t tag = CreateShaderTag(flavour);
|
|
|
|
auto pos = variants.find(tag);
|
|
|
|
if (pos == variants.end())
|
|
{
|
|
FString variantConfig = "\n";
|
|
|
|
variantConfig.AppendFormat("#define MAXIMUM_LIGHT_VECTORS %d\n", gles.numlightvectors);
|
|
variantConfig.AppendFormat("#define DEF_TEXTURE_MODE %d\n", flavour.textureMode);
|
|
variantConfig.AppendFormat("#define DEF_TEXTURE_FLAGS %d\n", flavour.texFlags);
|
|
variantConfig.AppendFormat("#define DEF_BLEND_FLAGS %d\n", flavour.blendFlags);
|
|
variantConfig.AppendFormat("#define DEF_FOG_2D %d\n", flavour.twoDFog);
|
|
variantConfig.AppendFormat("#define DEF_FOG_ENABLED %d\n", flavour.fogEnabled);
|
|
variantConfig.AppendFormat("#define DEF_FOG_RADIAL %d\n", flavour.fogEquationRadial);
|
|
variantConfig.AppendFormat("#define DEF_FOG_COLOURED %d\n", flavour.colouredFog);
|
|
variantConfig.AppendFormat("#define DEF_USE_U_LIGHT_LEVEL %d\n", flavour.useULightLevel);
|
|
|
|
variantConfig.AppendFormat("#define DEF_DO_DESATURATE %d\n", flavour.doDesaturate);
|
|
|
|
variantConfig.AppendFormat("#define DEF_DYNAMIC_LIGHTS_MOD %d\n", flavour.dynLightsMod);
|
|
variantConfig.AppendFormat("#define DEF_DYNAMIC_LIGHTS_SUB %d\n", flavour.dynLightsSub);
|
|
variantConfig.AppendFormat("#define DEF_DYNAMIC_LIGHTS_ADD %d\n", flavour.dynLightsAdd);
|
|
|
|
variantConfig.AppendFormat("#define DEF_USE_OBJECT_COLOR_2 %d\n", flavour.useObjectColor2);
|
|
variantConfig.AppendFormat("#define DEF_USE_GLOW_TOP_COLOR %d\n", flavour.useGlowTopColor);
|
|
variantConfig.AppendFormat("#define DEF_USE_GLOW_BOTTOM_COLOR %d\n", flavour.useGlowBottomColor);
|
|
|
|
variantConfig.AppendFormat("#define DEF_USE_COLOR_MAP %d\n", flavour.useColorMap);
|
|
variantConfig.AppendFormat("#define DEF_BUILD_LIGHTING %d\n", flavour.buildLighting);
|
|
variantConfig.AppendFormat("#define DEF_BANDED_SW_LIGHTING %d\n", flavour.bandedSwLight);
|
|
|
|
variantConfig.AppendFormat("#define USE_GLSL_V100 %d\n", gles.forceGLSLv100);
|
|
|
|
#ifdef NPOT_EMULATION
|
|
variantConfig.AppendFormat("#define DEF_NPOT_EMULATION %d\n", flavour.npotEmulation);
|
|
#endif
|
|
|
|
variantConfig.AppendFormat("#define DEF_HAS_SPOTLIGHT %d\n", flavour.hasSpotLight);
|
|
|
|
//Printf("Shader: %s, %08x %s", mFragProg2.GetChars(), tag, variantConfig.GetChars());
|
|
|
|
Load(mName.GetChars(), mVertProg, mFragProg, mFragProg2, mLightProg, mDefinesBase + variantConfig);
|
|
|
|
variants.insert(std::make_pair(tag, cur));
|
|
}
|
|
else
|
|
{
|
|
cur = pos->second;
|
|
}
|
|
|
|
GLRenderer->mShaderManager->SetActiveShader(this->cur);
|
|
return true;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Since all shaders are REQUIRED, any error here needs to be fatal
|
|
//
|
|
//==========================================================================
|
|
|
|
FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType)
|
|
{
|
|
FString defines;
|
|
defines += shaderdefines;
|
|
// this can't be in the shader code due to ATI strangeness.
|
|
if (!usediscard) defines += "#define NO_ALPHATEST\n";
|
|
|
|
FShader *shader = NULL;
|
|
try
|
|
{
|
|
shader = new FShader(ShaderName);
|
|
if (!shader->Configure(ShaderName, "shaders_gles/glsl/main.vp", "shaders_gles/glsl/main.fp", ShaderPath, LightModePath, defines.GetChars()))
|
|
{
|
|
I_FatalError("Unable to load shader %s\n", ShaderName);
|
|
}
|
|
}
|
|
catch(CRecoverableError &err)
|
|
{
|
|
if (shader != NULL) delete shader;
|
|
shader = NULL;
|
|
I_FatalError("Unable to load shader %s:\n%s\n", ShaderName, err.GetMessage());
|
|
}
|
|
return shader;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
FShaderManager::FShaderManager()
|
|
{
|
|
for (int passType = 0; passType < MAX_PASS_TYPES; passType++)
|
|
mPassShaders.Push(new FShaderCollection((EPassType)passType));
|
|
}
|
|
|
|
FShaderManager::~FShaderManager()
|
|
{
|
|
glUseProgram(0);
|
|
mActiveShader = NULL;
|
|
|
|
for (auto collection : mPassShaders)
|
|
delete collection;
|
|
}
|
|
|
|
void FShaderManager::SetActiveShader(FShader::ShaderVariantData *sh)
|
|
{
|
|
if (mActiveShader != sh)
|
|
{
|
|
glUseProgram(sh!= NULL? sh->GetHandle() : 0);
|
|
mActiveShader = sh;
|
|
}
|
|
}
|
|
|
|
FShader *FShaderManager::BindEffect(int effect, EPassType passType, ShaderFlavourData &flavour)
|
|
{
|
|
if (passType < mPassShaders.Size())
|
|
return mPassShaders[passType]->BindEffect(effect, flavour);
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
FShader *FShaderManager::Get(unsigned int eff, bool alphateston, EPassType passType)
|
|
{
|
|
if (passType < mPassShaders.Size())
|
|
return mPassShaders[passType]->Get(eff, alphateston);
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
FShaderCollection::FShaderCollection(EPassType passType)
|
|
{
|
|
CompileShaders(passType);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
FShaderCollection::~FShaderCollection()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void FShaderCollection::CompileShaders(EPassType passType)
|
|
{
|
|
mMaterialShaders.Clear();
|
|
mMaterialShadersNAT.Clear();
|
|
for (int i = 0; i < MAX_EFFECTS; i++)
|
|
{
|
|
mEffectShaders[i] = NULL;
|
|
}
|
|
|
|
for(int i=0;defaultshaders[i].ShaderName != NULL;i++)
|
|
{
|
|
FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, true, passType);
|
|
mMaterialShaders.Push(shc);
|
|
if (i < SHADER_NoTexture)
|
|
{
|
|
FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, false, passType);
|
|
mMaterialShadersNAT.Push(shc);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
for(unsigned i = 0; i < usershaders.Size(); i++)
|
|
{
|
|
FString name = ExtractFileBase(usershaders[i].shader);
|
|
FString defines = defaultshaders[usershaders[i].shaderType].Defines + usershaders[i].defines;
|
|
FShader *shc = Compile(name, usershaders[i].shader, defaultshaders[usershaders[i].shaderType].lightfunc, defines, true, passType);
|
|
mMaterialShaders.Push(shc);
|
|
}
|
|
#endif
|
|
|
|
for(int i=0;i<MAX_EFFECTS;i++)
|
|
{
|
|
FShader *eff = new FShader(effectshaders[i].ShaderName);
|
|
if (!eff->Configure(effectshaders[i].ShaderName, effectshaders[i].vp, effectshaders[i].fp1,
|
|
effectshaders[i].fp2, effectshaders[i].fp3, effectshaders[i].defines))
|
|
{
|
|
delete eff;
|
|
}
|
|
else mEffectShaders[i] = eff;
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void FShaderCollection::Clean()
|
|
{
|
|
for (unsigned int i = 0; i < mMaterialShadersNAT.Size(); i++)
|
|
{
|
|
if (mMaterialShadersNAT[i] != NULL) delete mMaterialShadersNAT[i];
|
|
}
|
|
for (unsigned int i = 0; i < mMaterialShaders.Size(); i++)
|
|
{
|
|
if (mMaterialShaders[i] != NULL) delete mMaterialShaders[i];
|
|
}
|
|
for (int i = 0; i < MAX_EFFECTS; i++)
|
|
{
|
|
if (mEffectShaders[i] != NULL) delete mEffectShaders[i];
|
|
mEffectShaders[i] = NULL;
|
|
}
|
|
mMaterialShaders.Clear();
|
|
mMaterialShadersNAT.Clear();
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
int FShaderCollection::Find(const char * shn)
|
|
{
|
|
FName sfn = shn;
|
|
|
|
for(unsigned int i=0;i<mMaterialShaders.Size();i++)
|
|
{
|
|
if (mMaterialShaders[i]->mName == sfn)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
FShader *FShaderCollection::BindEffect(int effect, ShaderFlavourData& flavour)
|
|
{
|
|
if (effect >= 0 && effect < MAX_EFFECTS && mEffectShaders[effect] != NULL)
|
|
{
|
|
mEffectShaders[effect]->Bind(flavour);
|
|
return mEffectShaders[effect];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void gl_DestroyUserShaders()
|
|
{
|
|
// todo
|
|
}
|
|
|
|
}
|