2019-10-04 21:29:00 +00:00
|
|
|
#include <memory>
|
2019-09-16 17:35:04 +00:00
|
|
|
#include "glbackend.h"
|
|
|
|
#include "glad/glad.h"
|
2019-09-16 20:56:48 +00:00
|
|
|
#include "gl_samplers.h"
|
2019-10-05 10:28:08 +00:00
|
|
|
#include "gl_shader.h"
|
2019-09-16 17:35:04 +00:00
|
|
|
|
2019-10-04 19:13:04 +00:00
|
|
|
#include "baselayer.h"
|
2019-10-04 21:29:00 +00:00
|
|
|
#include "resourcefile.h"
|
|
|
|
|
|
|
|
std::unique_ptr<FResourceFile> engine_res;
|
|
|
|
|
|
|
|
// The resourge manager in cache1d is far too broken to add some arbitrary file without some adjustment.
|
|
|
|
// For now, keep this file here, until the resource management can be redone in a more workable fashion.
|
|
|
|
extern FString progdir;
|
|
|
|
|
|
|
|
void InitBaseRes()
|
|
|
|
{
|
|
|
|
if (!engine_res)
|
|
|
|
{
|
|
|
|
// If we get here for the first time, load the engine-internal data.
|
|
|
|
FString baseres = progdir + "demolition.pk3";
|
|
|
|
engine_res.reset(FResourceFile::OpenResourceFile(baseres, true, true));
|
|
|
|
if (!engine_res)
|
|
|
|
{
|
|
|
|
wm_msgbox("Fatal error", "Engine resources (demolition.pk3) not found");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FileReader GetBaseResource(const char* fn)
|
|
|
|
{
|
|
|
|
auto lump = engine_res->FindLump(fn);
|
|
|
|
if (!lump)
|
|
|
|
{
|
|
|
|
wm_msgbox("Fatal error", "Base resource '%s' not found", fn);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
return lump->NewReader();
|
|
|
|
}
|
2019-09-23 21:33:59 +00:00
|
|
|
|
2019-09-16 17:35:04 +00:00
|
|
|
GLInstance GLInterface;
|
|
|
|
|
2019-09-16 20:56:48 +00:00
|
|
|
void GLInstance::Init()
|
|
|
|
{
|
2019-10-04 21:29:00 +00:00
|
|
|
InitBaseRes();
|
2019-09-18 18:44:21 +00:00
|
|
|
if (!mSamplers)
|
|
|
|
{
|
|
|
|
mSamplers = new FSamplerManager;
|
|
|
|
memset(LastBoundTextures, 0, sizeof(LastBoundTextures));
|
|
|
|
}
|
2019-10-04 19:13:04 +00:00
|
|
|
|
|
|
|
glinfo.vendor = (const char*)glGetString(GL_VENDOR);
|
|
|
|
glinfo.renderer = (const char*)glGetString(GL_RENDERER);
|
|
|
|
glinfo.version = (const char*)glGetString(GL_VERSION);
|
|
|
|
glinfo.extensions = (const char*)glGetString(GL_EXTENSIONS);
|
|
|
|
glinfo.bufferstorage = !!strstr(glinfo.extensions, "GL_ARB_buffer_storage");
|
|
|
|
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &glinfo.maxanisotropy);
|
|
|
|
if (!glinfo.dumped)
|
|
|
|
{
|
|
|
|
osdcmd_glinfo(NULL);
|
|
|
|
glinfo.dumped = 1;
|
|
|
|
}
|
2019-10-05 10:28:08 +00:00
|
|
|
new(&renderState) PolymostRenderState; // reset to defaults.
|
|
|
|
LoadPolymostShader();
|
2019-10-04 19:13:04 +00:00
|
|
|
|
2019-09-16 20:56:48 +00:00
|
|
|
}
|
|
|
|
|
2019-10-05 10:28:08 +00:00
|
|
|
void GLInstance::LoadPolymostShader()
|
|
|
|
{
|
|
|
|
auto fr1 = GetBaseResource("demolition/shaders/glsl/polymost.vp");
|
|
|
|
TArray<uint8_t> polymost1Vert = fr1.Read();
|
|
|
|
fr1 = GetBaseResource("demolition/shaders/glsl/polymost.fp");
|
|
|
|
TArray<uint8_t> polymost1Frag = fr1.Read();
|
|
|
|
// Zero-terminate both strings.
|
|
|
|
polymost1Vert.Push(0);
|
|
|
|
polymost1Frag.Push(0);
|
|
|
|
polymostShader = new PolymostShader();
|
|
|
|
if (!polymostShader->Load("PolymostShader", (const char*)polymost1Vert.Data(), (const char*)polymost1Frag.Data()))
|
|
|
|
{
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
SetPolymostShader();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-10-04 16:12:03 +00:00
|
|
|
void GLInstance::InitGLState(int fogmode, int multisample)
|
|
|
|
{
|
|
|
|
glShadeModel(GL_SMOOTH); // GL_FLAT
|
|
|
|
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
|
|
|
|
glDisable(GL_DITHER);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glHint(GL_FOG_HINT, GL_NICEST);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
|
|
|
|
glEnable(GL_DEPTH_CLAMP);
|
|
|
|
|
|
|
|
if (multisample > 0 )
|
|
|
|
{
|
|
|
|
glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
|
|
|
|
glEnable(GL_MULTISAMPLE);
|
|
|
|
}
|
|
|
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
|
|
|
}
|
|
|
|
|
2019-09-16 20:56:48 +00:00
|
|
|
void GLInstance::Deinit()
|
|
|
|
{
|
|
|
|
if (mSamplers) delete mSamplers;
|
2019-09-23 21:33:59 +00:00
|
|
|
mSamplers = nullptr;
|
2019-10-05 10:28:08 +00:00
|
|
|
if (polymostShader) delete polymostShader;
|
|
|
|
polymostShader = nullptr;
|
|
|
|
|
|
|
|
activeShader = nullptr;
|
2019-09-16 20:56:48 +00:00
|
|
|
}
|
2019-09-16 17:35:04 +00:00
|
|
|
|
|
|
|
std::pair<size_t, BaseVertex *> GLInstance::AllocVertices(size_t num)
|
|
|
|
{
|
|
|
|
Buffer.resize(num);
|
|
|
|
return std::make_pair((size_t)0, Buffer.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
static GLint primtypes[] =
|
|
|
|
{
|
|
|
|
GL_TRIANGLES,
|
|
|
|
GL_TRIANGLE_STRIP,
|
|
|
|
GL_TRIANGLE_FAN,
|
|
|
|
GL_QUADS,
|
|
|
|
GL_LINES
|
|
|
|
};
|
|
|
|
|
|
|
|
void GLInstance::Draw(EDrawType type, size_t start, size_t count)
|
|
|
|
{
|
2019-10-05 10:28:08 +00:00
|
|
|
if (activeShader == polymostShader) renderState.Apply(polymostShader);
|
2019-09-16 17:35:04 +00:00
|
|
|
glBegin(primtypes[type]);
|
|
|
|
auto p = &Buffer[start];
|
|
|
|
for (size_t i = 0; i < count; i++, p++)
|
|
|
|
{
|
|
|
|
glTexCoord2f(p->u, p->v);
|
|
|
|
glVertex3f(p->x, p->y, p->z);
|
|
|
|
}
|
|
|
|
glEnd();
|
|
|
|
}
|
2019-09-16 20:56:48 +00:00
|
|
|
|
2019-09-17 17:03:42 +00:00
|
|
|
int GLInstance::GetTextureID()
|
|
|
|
{
|
|
|
|
// Generating large numbers of texture IDs piece by piece does not work well on modern NVidia drivers.
|
|
|
|
|
|
|
|
if (currentindex == THCACHESIZE)
|
|
|
|
{
|
|
|
|
currentindex = 0;
|
|
|
|
glGenTextures(THCACHESIZE, TextureHandleCache);
|
|
|
|
}
|
|
|
|
else currentindex++;
|
|
|
|
return TextureHandleCache[currentindex];
|
|
|
|
}
|
2019-09-16 20:56:48 +00:00
|
|
|
|
2019-09-18 18:44:21 +00:00
|
|
|
FHardwareTexture* GLInstance::NewTexture()
|
|
|
|
{
|
|
|
|
return new FHardwareTexture;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::BindTexture(int texunit, FHardwareTexture *tex, int sampler)
|
2019-09-16 20:56:48 +00:00
|
|
|
{
|
2019-09-18 18:44:21 +00:00
|
|
|
if (!tex) return;
|
2019-09-17 17:03:42 +00:00
|
|
|
if (texunit != 0) glActiveTexture(GL_TEXTURE0 + texunit);
|
2019-09-18 18:44:21 +00:00
|
|
|
glBindTexture(GL_TEXTURE_2D, tex->GetTextureHandle());
|
|
|
|
mSamplers->Bind(texunit, sampler == NoSampler? tex->GetSampler() : sampler, 0);
|
2019-09-17 17:03:42 +00:00
|
|
|
if (texunit != 0) glActiveTexture(GL_TEXTURE0);
|
2019-09-18 18:44:21 +00:00
|
|
|
LastBoundTextures[texunit] = tex->GetTextureHandle();
|
2019-09-17 17:03:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::UnbindTexture(int texunit)
|
|
|
|
{
|
|
|
|
if (LastBoundTextures[texunit] != 0)
|
|
|
|
{
|
|
|
|
if (texunit != 0) glActiveTexture(GL_TEXTURE0+texunit);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
if (texunit != 0) glActiveTexture(GL_TEXTURE0);
|
|
|
|
LastBoundTextures[texunit] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::UnbindAllTextures()
|
|
|
|
{
|
|
|
|
for(int texunit = 0; texunit < MAX_TEXTURES; texunit++)
|
|
|
|
{
|
|
|
|
UnbindTexture(texunit);
|
|
|
|
}
|
|
|
|
}
|
2019-10-04 16:12:03 +00:00
|
|
|
|
|
|
|
void GLInstance::EnableBlend(bool on)
|
|
|
|
{
|
|
|
|
if (on) glEnable (GL_BLEND);
|
|
|
|
else glDisable (GL_BLEND);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::EnableAlphaTest(bool on)
|
|
|
|
{
|
|
|
|
if (on) glEnable (GL_ALPHA_TEST);
|
|
|
|
else glDisable (GL_ALPHA_TEST);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::EnableDepthTest(bool on)
|
|
|
|
{
|
|
|
|
if (on) glEnable (GL_DEPTH_TEST);
|
|
|
|
else glDisable (GL_DEPTH_TEST);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::SetMatrix(int num, const VSMatrix *mat)
|
|
|
|
{
|
|
|
|
matrices[num] = *mat;
|
|
|
|
switch(num)
|
|
|
|
{
|
2019-10-05 10:28:08 +00:00
|
|
|
default:
|
|
|
|
return;
|
|
|
|
case Matrix_View:
|
|
|
|
polymostShader->RotMatrix.Set(mat->get());
|
|
|
|
break;
|
|
|
|
|
2019-10-04 16:12:03 +00:00
|
|
|
case Matrix_Projection:
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
2019-10-05 10:28:08 +00:00
|
|
|
glLoadMatrixf(mat->get());
|
2019-10-04 16:12:03 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Matrix_ModelView:
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
2019-10-05 10:28:08 +00:00
|
|
|
glLoadMatrixf(mat->get());
|
2019-10-04 16:12:03 +00:00
|
|
|
break;
|
|
|
|
|
2019-10-05 10:28:08 +00:00
|
|
|
case Matrix_Texture3:
|
|
|
|
case Matrix_Texture4:
|
|
|
|
glActiveTexture(GL_TEXTURE3 + num - Matrix_Texture3);
|
2019-10-04 16:12:03 +00:00
|
|
|
glMatrixMode(GL_TEXTURE);
|
2019-10-05 10:28:08 +00:00
|
|
|
glLoadMatrixf(mat->get());
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
2019-10-04 16:12:03 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::EnableStencilWrite(int value)
|
|
|
|
{
|
|
|
|
glEnable(GL_STENCIL_TEST);
|
|
|
|
glClear(GL_STENCIL_BUFFER_BIT);
|
|
|
|
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
|
|
|
|
glStencilFunc(GL_ALWAYS, value, 0xFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::EnableStencilTest(int value)
|
|
|
|
{
|
|
|
|
glEnable(GL_STENCIL_TEST);
|
|
|
|
glStencilFunc(GL_EQUAL, value, 0xFF);
|
|
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::DisableStencil()
|
|
|
|
{
|
|
|
|
glDisable(GL_STENCIL_TEST);
|
|
|
|
}
|
|
|
|
|
2019-10-04 19:13:04 +00:00
|
|
|
void GLInstance::SetCull(int type, int winding)
|
2019-10-04 16:12:03 +00:00
|
|
|
{
|
|
|
|
if (type == Cull_None)
|
|
|
|
{
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
}
|
|
|
|
else if (type == Cull_Front)
|
|
|
|
{
|
2019-10-04 19:13:04 +00:00
|
|
|
glFrontFace(winding == Winding_CW ? GL_CW : GL_CCW);
|
2019-10-04 16:12:03 +00:00
|
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
glCullFace(GL_FRONT);
|
|
|
|
}
|
|
|
|
else if (type == Cull_Back)
|
|
|
|
{
|
2019-10-04 19:13:04 +00:00
|
|
|
glFrontFace(winding == Winding_CW ? GL_CW : GL_CCW);
|
2019-10-04 16:12:03 +00:00
|
|
|
glEnable(GL_CULL_FACE);
|
|
|
|
glCullFace(GL_BACK);
|
|
|
|
}
|
|
|
|
}
|
2019-10-04 16:25:18 +00:00
|
|
|
|
|
|
|
void GLInstance::SetColor(float r, float g, float b, float a)
|
|
|
|
{
|
|
|
|
glColor4f(r, g, b, a);
|
2019-10-04 16:44:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::SetDepthFunc(int func)
|
|
|
|
{
|
|
|
|
int f[] = { GL_ALWAYS, GL_LESS, GL_EQUAL, GL_LEQUAL };
|
|
|
|
glDepthFunc(f[func]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::SetFogLinear(float* color, float start, float end)
|
|
|
|
{
|
|
|
|
glFogi(GL_FOG_MODE, GL_LINEAR);
|
|
|
|
glFogf(GL_FOG_START, start);
|
|
|
|
glFogf(GL_FOG_END, end);
|
|
|
|
glFogfv(GL_FOG_COLOR, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::SetFogExp2(float* color, float coefficient)
|
|
|
|
{
|
|
|
|
glFogi(GL_FOG_MODE, GL_EXP2);
|
|
|
|
glFogf(GL_FOG_DENSITY, coefficient);
|
|
|
|
glFogfv(GL_FOG_COLOR, color);
|
|
|
|
}
|
2019-10-04 17:17:55 +00:00
|
|
|
|
|
|
|
void GLInstance::SetColorMask(bool on)
|
|
|
|
{
|
|
|
|
glColorMask(on, on, on, on);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::SetDepthMask(bool on)
|
|
|
|
{
|
|
|
|
glDepthMask(on);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int blendstyles[] = { GL_ZERO, GL_ONE, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA };
|
|
|
|
|
|
|
|
void GLInstance::SetBlendFunc(int src, int dst)
|
|
|
|
{
|
|
|
|
glBlendFunc(blendstyles[src], blendstyles[dst]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int renderops[] = { GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT };
|
|
|
|
|
|
|
|
void GLInstance::SetBlendOp(int op)
|
|
|
|
{
|
|
|
|
glBlendEquation(renderops[op]);
|
|
|
|
}
|
2019-10-04 19:13:04 +00:00
|
|
|
|
|
|
|
void GLInstance::ClearScreen(float r, float g, float b, bool depth)
|
|
|
|
{
|
|
|
|
glClearColor(r, g, b, 1.f);
|
|
|
|
glClear(depth ? GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT : GL_COLOR_BUFFER_BIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::ClearDepth()
|
|
|
|
{
|
|
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::SetAlphaThreshold(float al)
|
|
|
|
{
|
|
|
|
glAlphaFunc(GL_GREATER, al);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::SetViewport(int x, int y, int w, int h)
|
|
|
|
{
|
|
|
|
glViewport(x, y, w, h);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::SetWireframe(bool on)
|
|
|
|
{
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK,on? GL_LINE : GL_FILL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::ReadPixels(int xdim, int ydim, uint8_t* buffer)
|
|
|
|
{
|
|
|
|
glReadPixels(0, 0, xdim, ydim, GL_RGB, GL_UNSIGNED_BYTE, buffer);
|
2019-10-05 10:28:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GLInstance::SetPolymostShader()
|
|
|
|
{
|
|
|
|
if (activeShader != polymostShader)
|
|
|
|
{
|
|
|
|
polymostShader->Bind();
|
|
|
|
activeShader = polymostShader;
|
|
|
|
}
|
|
|
|
//GLInterface.BindTexture(1, palswapTextureID);
|
|
|
|
//GLInterface.BindTexture(2, paletteTextureIDs[curbasepal]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PolymostRenderState::Apply(PolymostShader* shader)
|
|
|
|
{
|
|
|
|
shader->Clamp.Set(Clamp);
|
|
|
|
shader->Shade.Set(Shade);
|
|
|
|
shader->NumShades.Set(NumShades);
|
|
|
|
shader->VisFactor.Set(VisFactor);
|
|
|
|
shader->FogEnabled.Set(FogEnabled);
|
|
|
|
shader->UseColorOnly.Set(UseColorOnly);
|
|
|
|
shader->UsePalette.Set(UsePalette);
|
|
|
|
shader->UseDetailMapping.Set(UseDetailMapping);
|
|
|
|
shader->UseGlowMapping.Set(UseGlowMapping);
|
|
|
|
shader->NPOTEmulation.Set(NPOTEmulation);
|
|
|
|
shader->NPOTEmulationFactor.Set(NPOTEmulationFactor);
|
|
|
|
shader->NPOTEmulationXOffset.Set(NPOTEmulationXOffset);
|
|
|
|
shader->ShadeInterpolate.Set(ShadeInterpolate);
|
|
|
|
shader->Brightness.Set(Brightness);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
|
|
|
static void polymost_setPalswap(uint32_t index)
|
|
|
|
{
|
|
|
|
static uint32_t lastPalswapIndex;
|
|
|
|
|
|
|
|
if (currentShaderProgramID != polymost1CurrentShaderProgramID)
|
|
|
|
return;
|
|
|
|
|
|
|
|
lastPalswapIndex = index;
|
|
|
|
polymost1PalswapPos.x = index * polymost1PalswapSize.x;
|
|
|
|
polymost1PalswapPos.y = floorf(polymost1PalswapPos.x);
|
|
|
|
polymost1PalswapPos = { polymost1PalswapPos.x - polymost1PalswapPos.y + (0.5f / PALSWAP_TEXTURE_SIZE),
|
|
|
|
polymost1PalswapPos.y * polymost1PalswapSize.y + (0.5f / PALSWAP_TEXTURE_SIZE) };
|
|
|
|
glUniform2f(polymost1PalswapPosLoc, polymost1PalswapPos.x, polymost1PalswapPos.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void polymost_setPalswapSize(uint32_t width, uint32_t height)
|
|
|
|
{
|
|
|
|
if (currentShaderProgramID != polymost1CurrentShaderProgramID)
|
|
|
|
return;
|
|
|
|
|
|
|
|
polymost1PalswapSize = { width * (1.f / PALSWAP_TEXTURE_SIZE),
|
|
|
|
height * (1.f / PALSWAP_TEXTURE_SIZE) };
|
|
|
|
|
|
|
|
polymost1PalswapInnerSize = { (width - 1) * (1.f / PALSWAP_TEXTURE_SIZE),
|
|
|
|
(height - 1) * (1.f / PALSWAP_TEXTURE_SIZE) };
|
|
|
|
|
|
|
|
glUniform2f(polymost1PalswapSizeLoc, polymost1PalswapInnerSize.x, polymost1PalswapInnerSize.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|