diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 89308f13fe525edeabd4cb24bfaeb7f8b84eb2a0..afbeaa1e7b1fc9b65d4a49c1348ff1ad139f31ad 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -530,10 +530,14 @@ if(${SRB2_CONFIG_HWRENDER})
 
 	set(SRB2_R_OPENGL_SOURCES
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_opengl/r_opengl.c
+		${CMAKE_CURRENT_SOURCE_DIR}/hardware/shaders/gl_shaders.c
+		${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_glcommon/r_glcommon.c
 	)
 
 	set(SRB2_R_OPENGL_HEADERS
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_opengl/r_opengl.h
+		${CMAKE_CURRENT_SOURCE_DIR}/hardware/shaders/gl_shaders.h
+		${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_glcommon/r_glcommon.h
 	)
 
 endif()
diff --git a/src/Makefile b/src/Makefile
index 09e37121258f5ff28dd9b6baebd2c41304870e25..58e7428faf80c4a004c30c42000da4a46e2270c3 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -663,7 +663,7 @@ ifdef MINGW
 all_dll: opengl_dll ds3d_dll fmod_dll openal_dll
 
 opengl_dll: $(BIN)/r_opengl.dll
-$(BIN)/r_opengl.dll: $(OBJDIR)/ogl_win.o $(OBJDIR)/r_opengl.o
+$(BIN)/r_opengl.dll: $(OBJDIR)/ogl_win.o $(OBJDIR)/r_opengl.o $(OBJDIR)/r_glcommon.o $(OBJDIR)/gl_shaders.o
 	-$(MKDIR) $(BIN)
 	@echo Linking R_OpenGL.dll...
 	$(CC) --shared  $^ -o $@ -g -Wl,--add-stdcall-alias -lgdi32 -static-libgcc
@@ -706,7 +706,14 @@ endif
 else
 ifdef SDL
 ifdef MINGW
+$(OBJDIR)/gl_shaders.o: hardware/shaders/gl_shaders.c hardware/shaders/gl_shaders.h \
+ hardware/r_opengl/r_opengl.h doomdef.h doomtype.h doomdata.h hardware/hw_dll.h
+	$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
+$(OBJDIR)/r_glcommon.o: hardware/r_glcommon/r_glcommon.c hardware/r_glcommon/r_glcommon.h \
+ doomdef.h doomtype.h doomdata.h hardware/hw_dll.h
+	$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
 $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \
+ hardware/r_glcommon/r_glcommon.h hardware/shaders/gl_shaders.h \
  doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_gpu.h screen.h \
  command.h hardware/hw_data.h hardware/hw_defs.h \
  hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \
@@ -716,7 +723,14 @@ $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h
 	$(echoName)
 	$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
 else
+$(OBJDIR)/gl_shaders.o: hardware/shaders/gl_shaders.c hardware/shaders/gl_shaders.h \
+ hardware/r_opengl/r_opengl.h doomdef.h doomtype.h doomdata.h hardware/hw_dll.h
+	$(CC) $(CFLAGS) $(WFLAGS) -I/usr/X11R6/include -c $< -o $@
+$(OBJDIR)/r_glcommon.o: hardware/r_glcommon/r_glcommon.c hardware/r_glcommon/r_glcommon.h \
+ doomdef.h doomtype.h doomdata.h hardware/hw_dll.h
+	$(CC) $(CFLAGS) $(WFLAGS) -I/usr/X11R6/include -c $< -o $@
 $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \
+ hardware/r_glcommon/r_glcommon.h hardware/shaders/gl_shaders.h \
  doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_gpu.h screen.h \
  command.h hardware/hw_data.h hardware/hw_defs.h \
  hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \
@@ -807,7 +821,14 @@ $(OBJDIR)/SRB2.res: win32/Srb2win.rc win32/afxres.h win32/resource.h
 ifdef MINGW
 ifndef SDL
 ifndef NOHW
+$(OBJDIR)/gl_shaders.o: hardware/shaders/gl_shaders.c hardware/shaders/gl_shaders.h \
+ hardware/r_opengl/r_opengl.h doomdef.h doomtype.h doomdata.h hardware/hw_dll.h
+	$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
+$(OBJDIR)/r_glcommon.o: hardware/r_glcommon/r_glcommon.c hardware/r_glcommon/r_glcommon.h \
+ doomdef.h doomtype.h doomdata.h hardware/hw_dll.h
+	$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
 $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \
+ hardware/r_glcommon/r_glcommon.h hardware/shaders/gl_shaders.h \
  doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_gpu.h screen.h \
  command.h hardware/hw_data.h hardware/hw_defs.h \
  hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \
diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h
index 981d15d1532543c078d0c8b91d1b3c63eada6fb6..7aecad8e3e29a1baa458a63b0a244ea39c1ca3e7 100644
--- a/src/hardware/hw_defs.h
+++ b/src/hardware/hw_defs.h
@@ -31,8 +31,6 @@
 #define GPU_DEFAULTMIX 0x00000000
 #define GPU_DEFAULTFOG 0xFF000000
 
-#define SCREENVERTS 10
-
 // RGBA Color components with float type ranging [ 0 ... 1 ]
 struct FRGBAFloat
 {
@@ -212,6 +210,9 @@ enum EFilterMode
 	GPU_TEXFILTER_MIXED3,
 };
 
+// Vertex count for post processing effects
+#define GPU_POSTIMGVERTS 10
+
 #ifdef GL_SHADERS
 // Predefined shader types
 enum
@@ -226,6 +227,11 @@ enum
 	SHADER_FOG,
 	SHADER_SKY,
 
+#ifdef HAVE_GLES2
+	SHADER_FADEMASK,
+	SHADER_FADEMASK_ADDITIVEANDSUBTRACTIVE,
+#endif
+
 	NUMBASESHADERS,
 };
 
diff --git a/src/hardware/hw_gpu.c b/src/hardware/hw_gpu.c
new file mode 100644
index 0000000000000000000000000000000000000000..5052148931ac0c16450e3d82d73201093b6591fd
--- /dev/null
+++ b/src/hardware/hw_gpu.c
@@ -0,0 +1,26 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2020 by Jaime "Lactozilla" Passos.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file hw_gpu.c
+/// \brief GPU low-level interface API
+
+#ifdef HWRENDER
+
+#include "r_opengl/r_opengl.h"
+
+// ==========================================================================
+// the hardware driver object
+// ==========================================================================
+struct GPURenderingAPI *GPU = NULL;
+
+void GPUInterface_Load(struct GPURenderingAPI **api)
+{
+	*api = &GLInterfaceAPI;
+}
+
+#endif // HWRENDER
diff --git a/src/hardware/hw_gpu.h b/src/hardware/hw_gpu.h
new file mode 100644
index 0000000000000000000000000000000000000000..03f522cfe87c1aa169c601119da7174c355bfe1a
--- /dev/null
+++ b/src/hardware/hw_gpu.h
@@ -0,0 +1,74 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2020 by Jaime "Lactozilla" Passos.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file hw_gpu.h
+/// \brief GPU low-level interface API
+
+#ifndef __HWR_GPU_H__
+#define __HWR_GPU_H__
+
+#include "../screen.h"
+#include "hw_data.h"
+#include "hw_defs.h"
+#include "hw_md2.h"
+
+struct GPURenderingAPI
+{
+	boolean (*Init) (void);
+	void (*FinishUpdate) (INT32 waitvbl);
+
+	void (*SetState) (INT32 State, INT32 Value);
+	void (*SetTransform) (FTransform *ptransform);
+	void (*SetBlend) (UINT32 PolyFlags);
+	void (*SetPalette) (RGBA_t *palette);
+	void (*ClearBuffer) (boolean ColorMask, boolean DepthMask, FRGBAFloat *ClearColor);
+
+	void (*DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags);
+	void (*DrawIndexedTriangles) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags, UINT32 *IndexArray);
+	void (*Draw2DLine) (F2DCoord *v1, F2DCoord *v2, RGBA_t Color);
+	void (*DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface);
+	void (*DrawSkyDome) (FSkyDome *sky);
+
+	void (*SetTexture) (HWRTexture_t *TexInfo);
+	void (*UpdateTexture) (HWRTexture_t *TexInfo);
+	void (*DeleteTexture) (HWRTexture_t *TexInfo);
+
+	void (*ClearTextureCache) (void);
+	INT32 (*GetTextureUsed) (void);
+
+	void (*CreateModelVBOs) (model_t *model);
+
+	void (*ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height, INT32 dst_stride, UINT16 *dst_data);
+	void (*GClipRect) (INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float nearclip);
+
+	void (*MakeScreenTexture) (void);
+	void (*MakeScreenFinalTexture) (void);
+	void (*FlushScreenTextures) (void);
+
+	void (*StartScreenWipe) (void);
+	void (*EndScreenWipe) (void);
+	void (*DoScreenWipe) (void);
+	void (*DrawIntermissionBG) (void);
+	void (*DrawScreenFinalTexture) (int width, int height);
+
+	void (*PostImgRedraw) (float points[GPU_POSTIMGVERTS][GPU_POSTIMGVERTS][2]);
+
+	boolean (*CompileShaders) (void);
+	void (*CleanShaders) (void);
+	void (*SetShader) (int type);
+	void (*UnSetShader) (void);
+
+	void (*SetShaderInfo) (INT32 info, INT32 value);
+	void (*LoadCustomShader) (int number, char *code, size_t size, boolean isfragment);
+};
+
+extern struct GPURenderingAPI *GPU;
+
+void GPUInterface_Load(struct GPURenderingAPI **api);
+
+#endif // __HWR_GPU_H__
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 5395f918aad5f2120e93b846a9e488eabf9aaf40..b63d0df61a260772312f92b2d682500c4b7fe0dc 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -6518,7 +6518,7 @@ void HWR_DoPostProcessor(player_t *player)
 	if (*type == postimg_water || *type == postimg_heat)
 	{
 		// 10 by 10 grid. 2 coordinates (xy)
-		float v[SCREENVERTS][SCREENVERTS][2];
+		float v[GPU_POSTIMGVERTS][GPU_POSTIMGVERTS][2];
 		static double disStart = 0;
 		UINT8 x, y;
 		INT32 WAVELENGTH;
@@ -6539,13 +6539,13 @@ void HWR_DoPostProcessor(player_t *player)
 			FREQUENCY = 4; // Lower is faster
 		}
 
-		for (x = 0; x < SCREENVERTS; x++)
+		for (x = 0; x < GPU_POSTIMGVERTS; x++)
 		{
-			for (y = 0; y < SCREENVERTS; y++)
+			for (y = 0; y < GPU_POSTIMGVERTS; y++)
 			{
 				// Change X position based on its Y position.
-				v[x][y][0] = (x/((float)(SCREENVERTS-1.0f)/9.0f))-4.5f + (float)sin((disStart+(y*WAVELENGTH))/FREQUENCY)/AMPLITUDE;
-				v[x][y][1] = (y/((float)(SCREENVERTS-1.0f)/9.0f))-4.5f;
+				v[x][y][0] = (x/((float)(GPU_POSTIMGVERTS-1.0f)/9.0f))-4.5f + (float)sin((disStart+(y*WAVELENGTH))/FREQUENCY)/AMPLITUDE;
+				v[x][y][1] = (y/((float)(GPU_POSTIMGVERTS-1.0f)/9.0f))-4.5f;
 			}
 		}
 		GPU->PostImgRedraw(v);
diff --git a/src/hardware/r_glcommon/r_glcommon.c b/src/hardware/r_glcommon/r_glcommon.c
new file mode 100644
index 0000000000000000000000000000000000000000..cf552d2fcf69cd5195273a56e6294a3422f2773d
--- /dev/null
+++ b/src/hardware/r_glcommon/r_glcommon.c
@@ -0,0 +1,1041 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2020 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file r_glcommon.c
+/// \brief Common OpenGL functions shared by OpenGL backends
+
+#include "r_glcommon.h"
+
+#include "../../doomdata.h"
+#include "../../doomtype.h"
+#include "../../doomdef.h"
+#include "../../console.h"
+
+#ifdef GL_SHADERS
+#include "../shaders/gl_shaders.h"
+#endif
+
+#include <stdarg.h>
+
+const GLubyte *GLVersion = NULL;
+const GLubyte *GLRenderer = NULL;
+const GLubyte *GLExtensions = NULL;
+
+GLint GLMajorVersion = 0;
+GLint GLMinorVersion = 0;
+
+// ==========================================================================
+//                                                                    GLOBALS
+// ==========================================================================
+
+RGBA_t GPUTexturePalette[256];
+GLint  GPUTextureFormat;
+GLint  GPUScreenWidth, GPUScreenHeight;
+GLbyte GPUScreenDepth;
+GLint  GPUMaximumAnisotropy;
+
+FTextureInfo *TexCacheTail = NULL;
+FTextureInfo *TexCacheHead = NULL;
+
+GLuint CurrentTexture = 0;
+GLuint BlankTexture = 0;
+
+GLuint ScreenTexture = 0;
+GLuint FinalScreenTexture = 0;
+GLuint WipeStartTexture = 0;
+GLuint WipeEndTexture = 0;
+
+UINT32 CurrentPolyFlags;
+
+GLboolean MipmappingEnabled = GL_FALSE;
+GLboolean ModelLightingEnabled = GL_FALSE;
+
+GLint MipmapMinFilter = GL_LINEAR;
+GLint MipmapMagFilter = GL_LINEAR;
+GLint AnisotropicFilter = 0;
+
+float NearClippingPlane = NZCLIP_PLANE;
+
+// ==========================================================================
+//                                                                 EXTENSIONS
+// ==========================================================================
+
+boolean GLExtension_multitexture;
+boolean GLExtension_vertex_buffer_object;
+boolean GLExtension_texture_filter_anisotropic;
+boolean GLExtension_vertex_program;
+boolean GLExtension_fragment_program;
+boolean GLExtension_shaders; // Not an extension on its own, but it is set if multiple extensions are available.
+
+static FExtensionList const ExtensionList[] = {
+	{"GL_ARB_multitexture", &GLExtension_multitexture},
+	{"GL_ARB_vertex_buffer_object", &GLExtension_vertex_buffer_object},
+	{"GL_ARB_texture_filter_anisotropic", &GLExtension_texture_filter_anisotropic},
+	{"GL_EXT_texture_filter_anisotropic", &GLExtension_texture_filter_anisotropic},
+	{"GL_ARB_vertex_program", &GLExtension_vertex_program},
+	{"GL_ARB_fragment_program", &GLExtension_fragment_program},
+	{NULL, NULL}
+};
+
+static void PrintExtensions(const GLubyte *extensions);
+
+// ==========================================================================
+//                                                           OPENGL FUNCTIONS
+// ==========================================================================
+
+#ifndef STATIC_OPENGL
+/* Miscellaneous */
+PFNglClear pglClear;
+PFNglGetFloatv pglGetFloatv;
+PFNglGetIntegerv pglGetIntegerv;
+PFNglGetString pglGetString;
+PFNglClearColor pglClearColor;
+PFNglColorMask pglColorMask;
+PFNglAlphaFunc pglAlphaFunc;
+PFNglBlendFunc pglBlendFunc;
+PFNglCullFace pglCullFace;
+PFNglPolygonOffset pglPolygonOffset;
+PFNglEnable pglEnable;
+PFNglDisable pglDisable;
+
+/* Depth buffer */
+PFNglDepthFunc pglDepthFunc;
+PFNglDepthMask pglDepthMask;
+
+/* Transformation */
+PFNglViewport pglViewport;
+
+/* Raster functions */
+PFNglPixelStorei pglPixelStorei;
+PFNglReadPixels pglReadPixels;
+
+/* Texture mapping */
+PFNglTexParameteri pglTexParameteri;
+PFNglTexImage2D pglTexImage2D;
+PFNglTexSubImage2D pglTexSubImage2D;
+
+/* Drawing functions */
+PFNglDrawArrays pglDrawArrays;
+PFNglDrawElements pglDrawElements;
+
+/* Texture objects */
+PFNglGenTextures pglGenTextures;
+PFNglDeleteTextures pglDeleteTextures;
+PFNglBindTexture pglBindTexture;
+
+/* Texture mapping */
+PFNglCopyTexImage2D pglCopyTexImage2D;
+PFNglCopyTexSubImage2D pglCopyTexSubImage2D;
+
+/* 1.3 functions for multitexturing */
+PFNglActiveTexture pglActiveTexture;
+#endif
+
+//
+// Multi-texturing
+//
+
+#ifndef HAVE_GLES2
+#ifndef STATIC_OPENGL
+PFNglClientActiveTexture pglClientActiveTexture;
+#endif
+#endif
+
+//
+// Mipmapping
+//
+
+#ifdef HAVE_GLES
+PFNglGenerateMipmap pglGenerateMipmap;
+#endif
+
+//
+// Depth functions
+//
+#ifndef HAVE_GLES
+	PFNglClearDepth pglClearDepth;
+	PFNglDepthRange pglDepthRange;
+#else
+	PFNglClearDepthf pglClearDepthf;
+	PFNglDepthRangef pglDepthRangef;
+#endif
+
+//
+// Legacy functions
+//
+
+#ifndef HAVE_GLES2
+#ifndef STATIC_OPENGL
+/* Transformation */
+PFNglMatrixMode pglMatrixMode;
+PFNglViewport pglViewport;
+PFNglPushMatrix pglPushMatrix;
+PFNglPopMatrix pglPopMatrix;
+PFNglLoadIdentity pglLoadIdentity;
+PFNglMultMatrixf pglMultMatrixf;
+PFNglRotatef pglRotatef;
+PFNglScalef pglScalef;
+PFNglTranslatef pglTranslatef;
+
+/* Drawing functions */
+PFNglVertexPointer pglVertexPointer;
+PFNglNormalPointer pglNormalPointer;
+PFNglTexCoordPointer pglTexCoordPointer;
+PFNglColorPointer pglColorPointer;
+PFNglEnableClientState pglEnableClientState;
+PFNglDisableClientState pglDisableClientState;
+
+/* Lighting */
+PFNglShadeModel pglShadeModel;
+PFNglLightfv pglLightfv;
+PFNglLightModelfv pglLightModelfv;
+PFNglMaterialfv pglMaterialfv;
+
+/* Texture mapping */
+PFNglTexEnvi pglTexEnvi;
+#endif
+#endif // HAVE_GLES2
+
+// Color
+#ifdef HAVE_GLES
+PFNglColor4f pglColor4f;
+#else
+PFNglColor4ubv pglColor4ubv;
+#endif
+
+/* 1.5 functions for buffers */
+PFNglGenBuffers pglGenBuffers;
+PFNglBindBuffer pglBindBuffer;
+PFNglBufferData pglBufferData;
+PFNglDeleteBuffers pglDeleteBuffers;
+
+/* 2.0 functions */
+PFNglBlendEquation pglBlendEquation;
+
+// ==========================================================================
+//                                                                  FUNCTIONS
+// ==========================================================================
+
+boolean GLBackend_Init(void)
+{
+	return GLBackend_LoadFunctions();
+}
+
+boolean GLBackend_InitContext(void)
+{
+	if (GLVersion == NULL || GLRenderer == NULL)
+	{
+		GLVersion = pglGetString(GL_VERSION);
+		GLRenderer = pglGetString(GL_RENDERER);
+
+		pglGetIntegerv(GL_MAJOR_VERSION, &GLMajorVersion);
+		pglGetIntegerv(GL_MINOR_VERSION, &GLMinorVersion);
+
+		GL_DBG_Printf("OpenGL %s\n", GLVersion);
+		GL_DBG_Printf("Version: %d.%d\n", GLMajorVersion, GLMinorVersion);
+		GL_DBG_Printf("GPU: %s\n", GLRenderer);
+
+#if !defined(__ANDROID__)
+		if (strcmp((const char*)GLRenderer, "GDI Generic") == 0 &&
+			strcmp((const char*)GLVersion, "1.1.0") == 0)
+		{
+			// Oh no... Windows gave us the GDI Generic rasterizer, so something is wrong...
+			// The game will crash later on when unsupported OpenGL commands are encountered.
+			// Instead of a nondescript crash, show a more informative error message.
+			// Also set the renderer variable back to software so the next launch won't
+			// repeat this error.
+			CV_StealthSet(&cv_renderer, "Software");
+			I_Error("OpenGL Error: Failed to access the GPU. There may be an issue with your graphics drivers.");
+		}
+#endif
+	}
+
+	if (GLExtensions == NULL)
+		GLExtension_Init();
+
+	return true;
+}
+
+boolean GLBackend_LoadCommonFunctions(void)
+{
+#define GETOPENGLFUNC(func) \
+	p ## gl ## func = GLBackend_GetFunction("gl" #func); \
+	if (!(p ## gl ## func)) \
+	{ \
+		GL_MSG_Error("failed to get OpenGL function: %s", #func); \
+		return false; \
+	} \
+
+	GETOPENGLFUNC(ClearColor)
+
+	GETOPENGLFUNC(Clear)
+	GETOPENGLFUNC(ColorMask)
+	GETOPENGLFUNC(AlphaFunc)
+	GETOPENGLFUNC(BlendFunc)
+	GETOPENGLFUNC(CullFace)
+	GETOPENGLFUNC(PolygonOffset)
+	GETOPENGLFUNC(Enable)
+	GETOPENGLFUNC(Disable)
+	GETOPENGLFUNC(GetFloatv)
+	GETOPENGLFUNC(GetIntegerv)
+	GETOPENGLFUNC(GetString)
+
+	GETOPENGLFUNC(DepthFunc)
+	GETOPENGLFUNC(DepthMask)
+
+	GETOPENGLFUNC(Viewport)
+
+	GETOPENGLFUNC(DrawArrays)
+	GETOPENGLFUNC(DrawElements)
+
+	GETOPENGLFUNC(PixelStorei)
+	GETOPENGLFUNC(ReadPixels)
+
+	GETOPENGLFUNC(TexParameteri)
+	GETOPENGLFUNC(TexImage2D)
+	GETOPENGLFUNC(TexSubImage2D)
+
+	GETOPENGLFUNC(GenTextures)
+	GETOPENGLFUNC(DeleteTextures)
+	GETOPENGLFUNC(BindTexture)
+
+	GETOPENGLFUNC(CopyTexImage2D)
+	GETOPENGLFUNC(CopyTexSubImage2D)
+
+	return true;
+}
+
+boolean GLBackend_LoadLegacyFunctions(void)
+{
+#ifndef HAVE_GLES2
+	GETOPENGLFUNC(MatrixMode)
+	GETOPENGLFUNC(Viewport)
+	GETOPENGLFUNC(PushMatrix)
+	GETOPENGLFUNC(PopMatrix)
+	GETOPENGLFUNC(LoadIdentity)
+	GETOPENGLFUNC(MultMatrixf)
+	GETOPENGLFUNC(Rotatef)
+	GETOPENGLFUNC(Scalef)
+	GETOPENGLFUNC(Translatef)
+
+	GETOPENGLFUNC(ShadeModel)
+	GETOPENGLFUNC(Lightfv)
+	GETOPENGLFUNC(LightModelfv)
+	GETOPENGLFUNC(Materialfv)
+#endif
+
+	return true;
+}
+
+#undef GETOPENGLFUNC
+
+INT32 GLBackend_GetShaderType(INT32 type)
+{
+#ifdef GL_SHADERS
+	// If using model lighting, set the appropriate shader.
+	// However don't override a custom shader.
+	if (type == SHADER_MODEL && ModelLightingEnabled
+	&& !(ShaderObjects[SHADER_MODEL].custom && !ShaderObjects[SHADER_MODEL_LIGHTING].custom))
+		return SHADER_MODEL_LIGHTING;
+#endif
+
+	return type;
+}
+
+static void SetBlendEquation(GLenum mode)
+{
+	if (pglBlendEquation)
+		pglBlendEquation(mode);
+}
+
+static void SetBlendMode(UINT32 flags)
+{
+	// Set blending function
+	switch (flags)
+	{
+		case PF_Translucent & PF_Blending:
+			pglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // alpha = level of transparency
+			break;
+		case PF_Masked & PF_Blending:
+			// Hurdler: does that mean lighting is only made by alpha src?
+			// it sounds ok, but not for polygonsmooth
+			pglBlendFunc(GL_SRC_ALPHA, GL_ZERO);                // 0 alpha = holes in texture
+			break;
+		case PF_Additive & PF_Blending:
+		case PF_Subtractive & PF_Blending:
+		case PF_ReverseSubtract & PF_Blending:
+		case PF_Environment & PF_Blending:
+			pglBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+			break;
+		case PF_AdditiveSource & PF_Blending:
+			pglBlendFunc(GL_SRC_ALPHA, GL_ONE); // src * alpha + dest
+			break;
+		case PF_Multiplicative & PF_Blending:
+			pglBlendFunc(GL_DST_COLOR, GL_ZERO);
+			break;
+		case PF_Fog & PF_Fog:
+			// Sryder: Fog
+			// multiplies input colour by input alpha, and destination colour by input colour, then adds them
+			pglBlendFunc(GL_SRC_ALPHA, GL_SRC_COLOR);
+			break;
+		default: // must be 0, otherwise it's an error
+			// No blending
+			pglBlendFunc(GL_ONE, GL_ZERO);   // the same as no blending
+			break;
+	}
+
+	// Set blending equation
+	switch (flags)
+	{
+		case PF_Subtractive & PF_Blending:
+			SetBlendEquation(GL_FUNC_SUBTRACT);
+			break;
+		case PF_ReverseSubtract & PF_Blending:
+			// good for shadow
+			// not really but what else ?
+			SetBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
+			break;
+		default:
+			SetBlendEquation(GL_FUNC_ADD);
+			break;
+	}
+
+	// Alpha test
+	switch (flags)
+	{
+		case PF_Masked & PF_Blending:
+			pglAlphaFunc(GL_GREATER, 0.5f);
+			break;
+		case PF_Translucent & PF_Blending:
+		case PF_Additive & PF_Blending:
+		case PF_AdditiveSource & PF_Blending:
+		case PF_Subtractive & PF_Blending:
+		case PF_ReverseSubtract & PF_Blending:
+		case PF_Environment & PF_Blending:
+		case PF_Multiplicative & PF_Blending:
+			pglAlphaFunc(GL_NOTEQUAL, 0.0f);
+			break;
+		case PF_Fog & PF_Fog:
+			pglAlphaFunc(GL_ALWAYS, 0.0f); // Don't discard zero alpha fragments
+			break;
+		default:
+			pglAlphaFunc(GL_GREATER, 0.5f);
+			break;
+	}
+}
+
+// PF_Masked - we could use an ALPHA_TEST of GL_EQUAL, and alpha ref of 0,
+//             is it faster when pixels are discarded ?
+
+void SetBlendingStates(UINT32 PolyFlags)
+{
+	UINT32 Xor = CurrentPolyFlags^PolyFlags;
+
+	if (Xor & (PF_Blending|PF_RemoveYWrap|PF_ForceWrapX|PF_ForceWrapY|PF_Occlude|PF_NoTexture|PF_Modulated|PF_NoDepthTest|PF_Decal|PF_Invisible))
+	{
+		if (Xor & PF_Blending) // if blending mode must be changed
+			SetBlendMode(PolyFlags & PF_Blending);
+
+#ifndef HAVE_GLES2
+		if (Xor & PF_NoAlphaTest)
+		{
+			if (PolyFlags & PF_NoAlphaTest)
+				pglDisable(GL_ALPHA_TEST);
+			else
+				pglEnable(GL_ALPHA_TEST);      // discard 0 alpha pixels (holes in texture)
+		}
+#endif
+
+		if (Xor & PF_Decal)
+		{
+			if (PolyFlags & PF_Decal)
+				pglEnable(GL_POLYGON_OFFSET_FILL);
+			else
+				pglDisable(GL_POLYGON_OFFSET_FILL);
+		}
+
+		if (Xor & PF_NoDepthTest)
+		{
+			if (PolyFlags & PF_NoDepthTest)
+				pglDepthFunc(GL_ALWAYS);
+			else
+				pglDepthFunc(GL_LEQUAL);
+		}
+
+		if (Xor & PF_RemoveYWrap)
+		{
+			if (PolyFlags & PF_RemoveYWrap)
+				SetClamp(GL_TEXTURE_WRAP_T);
+		}
+
+		if (Xor & PF_ForceWrapX)
+		{
+			if (PolyFlags & PF_ForceWrapX)
+				pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+		}
+
+		if (Xor & PF_ForceWrapY)
+		{
+			if (PolyFlags & PF_ForceWrapY)
+				pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+		}
+
+#ifndef HAVE_GLES2
+		if (Xor & PF_Modulated)
+		{
+			if (PolyFlags & PF_Modulated)
+			{   // mix texture colour with Surface->PolyColor
+				pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+			}
+			else
+			{   // colour from texture is unchanged before blending
+				pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+			}
+		}
+#endif
+
+		if (Xor & PF_Occlude) // depth test but (no) depth write
+		{
+			if (PolyFlags&PF_Occlude)
+			{
+				pglDepthMask(1);
+			}
+			else
+				pglDepthMask(0);
+		}
+		////Hurdler: not used if we don't define POLYSKY
+		if (Xor & PF_Invisible)
+		{
+			if (PolyFlags&PF_Invisible)
+				pglBlendFunc(GL_ZERO, GL_ONE);         // transparent blending
+			else
+			{   // big hack: (TODO: manage that better)
+				// we test only for PF_Masked because PF_Invisible is only used
+				// (for now) with it (yeah, that's crappy, sorry)
+				if ((PolyFlags&PF_Blending)==PF_Masked)
+					pglBlendFunc(GL_SRC_ALPHA, GL_ZERO);
+			}
+		}
+		if (PolyFlags & PF_NoTexture)
+		{
+			SetNoTexture();
+		}
+	}
+
+	CurrentPolyFlags = PolyFlags;
+}
+
+void SetSurface(INT32 w, INT32 h)
+{
+	SetModelView(w, h);
+	SetStates();
+	pglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+void GLExtension_Init(void)
+{
+	INT32 i = 0;
+
+	GLExtensions = pglGetString(GL_EXTENSIONS);
+
+	GL_DBG_Printf("Extensions: ");
+	PrintExtensions(GLExtensions);
+
+	while (ExtensionList[i].name)
+	{
+		const FExtensionList *ext = &ExtensionList[i];
+
+		if (GLExtension_Available(ext->name))
+		{
+			(*ext->extension) = true;
+			GL_DBG_Printf("Extension %s is supported\n", ext->name);
+		}
+		else
+		{
+			(*ext->extension) = false;
+			GL_DBG_Printf("Extension %s is unsupported\n", ext->name);
+		}
+
+		i++;
+	}
+
+#ifdef GL_SHADERS
+	if (GLExtension_vertex_program && GLExtension_fragment_program && GLBackend_GetFunction("glUseProgram"))
+		GLExtension_shaders = true;
+#endif
+
+	if (GLExtension_texture_filter_anisotropic)
+		pglGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &GPUMaximumAnisotropy);
+	else
+		GPUMaximumAnisotropy = 0;
+}
+
+boolean GLExtension_Available(const char *extension)
+{
+#if defined(HAVE_GLES) && defined(HAVE_SDL)
+	return (SDL_GL_ExtensionSupported(extension) == SDL_TRUE ? true : false);
+#else
+	const GLubyte *start = GLExtensions;
+	GLubyte       *where, *terminator;
+
+	if (!extension || !start)
+		return false;
+
+	where = (GLubyte *) strchr(extension, ' ');
+	if (where || *extension == '\0')
+		return false;
+
+	for (;;)
+	{
+		where = (GLubyte *) strstr((const char *) start, extension);
+		if (!where)
+			break;
+		terminator = where + strlen(extension);
+		if (where == start || *(where - 1) == ' ')
+			if (*terminator == ' ' || *terminator == '\0')
+				return true;
+		start = terminator;
+	}
+
+	return false;
+#endif
+}
+
+static void PrintExtensions(const GLubyte *extensions)
+{
+	size_t size = strlen((const char *)extensions) + 1;
+	char *tk, *ext = calloc(size, sizeof(char));
+
+	memcpy(ext, extensions, size);
+	tk = strtok(ext, " ");
+
+	while (tk)
+	{
+		GL_DBG_Printf("%s", tk);
+		tk = strtok(NULL, " ");
+		if (tk)
+			GL_DBG_Printf(" ", tk);
+	}
+
+	GL_DBG_Printf("\n");
+	free(ext);
+}
+
+static size_t lerpBufferSize = 0;
+float *vertBuffer = NULL;
+float *normBuffer = NULL;
+
+static size_t lerpTinyBufferSize = 0;
+short *vertTinyBuffer = NULL;
+char *normTinyBuffer = NULL;
+
+// Static temporary buffer for doing frame interpolation
+// 'size' is the vertex size
+void GLModel_AllocLerpBuffer(size_t size)
+{
+	if (lerpBufferSize >= size)
+		return;
+
+	if (vertBuffer != NULL)
+		free(vertBuffer);
+
+	if (normBuffer != NULL)
+		free(normBuffer);
+
+	lerpBufferSize = size;
+	vertBuffer = malloc(lerpBufferSize);
+	normBuffer = malloc(lerpBufferSize);
+}
+
+// Static temporary buffer for doing frame interpolation
+// 'size' is the vertex size
+void GLModel_AllocLerpTinyBuffer(size_t size)
+{
+	if (lerpTinyBufferSize >= size)
+		return;
+
+	if (vertTinyBuffer != NULL)
+		free(vertTinyBuffer);
+
+	if (normTinyBuffer != NULL)
+		free(normTinyBuffer);
+
+	lerpTinyBufferSize = size;
+	vertTinyBuffer = malloc(lerpTinyBufferSize);
+	normTinyBuffer = malloc(lerpTinyBufferSize / 2);
+}
+
+static void CreateModelVBO(mesh_t *mesh, mdlframe_t *frame)
+{
+	int bufferSize = sizeof(vbo64_t)*mesh->numTriangles * 3;
+	vbo64_t *buffer = (vbo64_t*)malloc(bufferSize);
+	vbo64_t *bufPtr = buffer;
+
+	float *vertPtr = frame->vertices;
+	float *normPtr = frame->normals;
+	float *tanPtr = frame->tangents;
+	float *uvPtr = mesh->uvs;
+	float *lightPtr = mesh->lightuvs;
+	char *colorPtr = frame->colors;
+
+	int i;
+	for (i = 0; i < mesh->numTriangles * 3; i++)
+	{
+		bufPtr->x = *vertPtr++;
+		bufPtr->y = *vertPtr++;
+		bufPtr->z = *vertPtr++;
+
+		bufPtr->nx = *normPtr++;
+		bufPtr->ny = *normPtr++;
+		bufPtr->nz = *normPtr++;
+
+		bufPtr->s0 = *uvPtr++;
+		bufPtr->t0 = *uvPtr++;
+
+		if (tanPtr != NULL)
+		{
+			bufPtr->tan0 = *tanPtr++;
+			bufPtr->tan1 = *tanPtr++;
+			bufPtr->tan2 = *tanPtr++;
+		}
+
+		if (lightPtr != NULL)
+		{
+			bufPtr->s1 = *lightPtr++;
+			bufPtr->t1 = *lightPtr++;
+		}
+
+		if (colorPtr)
+		{
+			bufPtr->r = *colorPtr++;
+			bufPtr->g = *colorPtr++;
+			bufPtr->b = *colorPtr++;
+			bufPtr->a = *colorPtr++;
+		}
+		else
+		{
+			bufPtr->r = 255;
+			bufPtr->g = 255;
+			bufPtr->b = 255;
+			bufPtr->a = 255;
+		}
+
+		bufPtr++;
+	}
+
+	pglGenBuffers(1, &frame->vboID);
+	pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID);
+	pglBufferData(GL_ARRAY_BUFFER, bufferSize, buffer, GL_STATIC_DRAW);
+	free(buffer);
+
+	// Don't leave the array buffer bound to the model,
+	// since this is called mid-frame
+	pglBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+static void CreateModelVBOTiny(mesh_t *mesh, tinyframe_t *frame)
+{
+	int bufferSize = sizeof(vbotiny_t)*mesh->numTriangles * 3;
+	vbotiny_t *buffer = (vbotiny_t*)malloc(bufferSize);
+	vbotiny_t *bufPtr = buffer;
+
+	short *vertPtr = frame->vertices;
+	char *normPtr = frame->normals;
+	float *uvPtr = mesh->uvs;
+	char *tanPtr = frame->tangents;
+
+	int i;
+	for (i = 0; i < mesh->numVertices; i++)
+	{
+		bufPtr->x = *vertPtr++;
+		bufPtr->y = *vertPtr++;
+		bufPtr->z = *vertPtr++;
+
+		bufPtr->nx = *normPtr++;
+		bufPtr->ny = *normPtr++;
+		bufPtr->nz = *normPtr++;
+
+		bufPtr->s0 = *uvPtr++;
+		bufPtr->t0 = *uvPtr++;
+
+		if (tanPtr)
+		{
+			bufPtr->tanx = *tanPtr++;
+			bufPtr->tany = *tanPtr++;
+			bufPtr->tanz = *tanPtr++;
+		}
+
+		bufPtr++;
+	}
+
+	pglGenBuffers(1, &frame->vboID);
+	pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID);
+	pglBufferData(GL_ARRAY_BUFFER, bufferSize, buffer, GL_STATIC_DRAW);
+	free(buffer);
+
+	// Don't leave the array buffer bound to the model,
+	// since this is called mid-frame
+	pglBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+void GLModel_GenerateVBOs(model_t *model)
+{
+	int i;
+
+	if (!GLExtension_vertex_buffer_object)
+		return;
+
+	for (i = 0; i < model->numMeshes; i++)
+	{
+		mesh_t *mesh = &model->meshes[i];
+
+		if (mesh->frames)
+		{
+			int j;
+			for (j = 0; j < model->meshes[i].numFrames; j++)
+			{
+				mdlframe_t *frame = &mesh->frames[j];
+				if (frame->vboID)
+					pglDeleteBuffers(1, &frame->vboID);
+				frame->vboID = 0;
+				CreateModelVBO(mesh, frame);
+			}
+		}
+		else if (mesh->tinyframes)
+		{
+			int j;
+			for (j = 0; j < model->meshes[i].numFrames; j++)
+			{
+				tinyframe_t *frame = &mesh->tinyframes[j];
+				if (frame->vboID)
+					pglDeleteBuffers(1, &frame->vboID);
+				frame->vboID = 0;
+				CreateModelVBOTiny(mesh, frame);
+			}
+		}
+	}
+}
+
+// Deletes all textures.
+void GLTexture_Flush(void)
+{
+	while (TexCacheHead)
+	{
+		FTextureInfo *pTexInfo = TexCacheHead;
+		HWRTexture_t *texture = pTexInfo->texture;
+
+		if (pTexInfo->name)
+		{
+			pglDeleteTextures(1, (GLuint *)&pTexInfo->name);
+			pTexInfo->name = 0;
+		}
+
+		if (texture)
+			texture->downloaded = 0;
+
+		TexCacheHead = pTexInfo->next;
+		free(pTexInfo);
+	}
+
+	TexCacheTail = TexCacheHead = NULL; // Hurdler: well, TexCacheHead is already NULL
+	CurrentTexture = 0;
+}
+
+// Sets texture filtering mode.
+void GLTexture_SetFilterMode(INT32 mode)
+{
+	switch (mode)
+	{
+		case GPU_TEXFILTER_TRILINEAR:
+			MipmapMinFilter = GL_LINEAR_MIPMAP_LINEAR;
+			MipmapMagFilter = GL_LINEAR;
+			MipmappingEnabled = GL_TRUE;
+			break;
+		case GPU_TEXFILTER_BILINEAR:
+			MipmapMinFilter = MipmapMagFilter = GL_LINEAR;
+			MipmappingEnabled = GL_FALSE;
+			break;
+		case GPU_TEXFILTER_POINTSAMPLED:
+			MipmapMinFilter = MipmapMagFilter = GL_NEAREST;
+			MipmappingEnabled = GL_FALSE;
+			break;
+		case GPU_TEXFILTER_MIXED1:
+			MipmapMinFilter = GL_NEAREST;
+			MipmapMagFilter = GL_LINEAR;
+			MipmappingEnabled = GL_FALSE;
+			break;
+		case GPU_TEXFILTER_MIXED2:
+			MipmapMinFilter = GL_LINEAR;
+			MipmapMagFilter = GL_NEAREST;
+			MipmappingEnabled = GL_FALSE;
+			break;
+		case GPU_TEXFILTER_MIXED3:
+			MipmapMinFilter = GL_LINEAR_MIPMAP_LINEAR;
+			MipmapMagFilter = GL_NEAREST;
+			MipmappingEnabled = GL_TRUE;
+			break;
+		default:
+			MipmapMagFilter = GL_LINEAR;
+			MipmapMinFilter = GL_NEAREST;
+	}
+}
+
+// Deletes a single texture.
+void GLTexture_Delete(HWRTexture_t *pTexInfo)
+{
+	FTextureInfo *head = TexCacheHead;
+
+	while (head)
+	{
+		if (head->name == pTexInfo->downloaded)
+		{
+			if (head->next)
+				head->next->prev = head->prev;
+			if (head->prev)
+				head->prev->next = head->next;
+			free(head);
+			break;
+		}
+
+		head = head->next;
+	}
+}
+
+// Calculates total memory usage by textures, excluding mipmaps.
+INT32 GLTexture_GetMemoryUsage(FTextureInfo *head)
+{
+	INT32 res = 0;
+
+	while (head)
+	{
+		// Figure out the correct bytes-per-pixel for this texture
+		// This follows format2bpp in hw_cache.c
+		INT32 bpp = 1;
+		UINT32 format = head->format;
+		if (format == GPU_TEXFMT_RGBA)
+			bpp = 4;
+		else if (format == GPU_TEXFMT_ALPHA_INTENSITY_88 || format == GPU_TEXFMT_AP_88)
+			bpp = 2;
+
+		// Add it up!
+		res += head->height*head->width*bpp;
+		head = head->next;
+	}
+
+	return res;
+}
+
+// Generates a screen texture.
+void GLTexture_GenerateScreenTexture(GLuint *name)
+{
+	INT32 texsize = 2048;
+	boolean firstTime = ((*name) == 0);
+
+	// Use a power of two texture, dammit
+	if (GPUScreenWidth <= 512)
+		texsize = 512;
+	else if (GPUScreenWidth <= 1024)
+		texsize = 1024;
+
+	// Create screen texture
+	if (firstTime)
+		pglGenTextures(1, name);
+	pglBindTexture(GL_TEXTURE_2D, *name);
+
+	if (firstTime)
+	{
+		pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+		pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+		SetClamp(GL_TEXTURE_WRAP_S);
+		SetClamp(GL_TEXTURE_WRAP_T);
+		pglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, texsize, texsize, 0);
+	}
+	else
+		pglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, texsize, texsize);
+
+	CurrentTexture = *name;
+}
+
+// -----------------+
+// GL_DBG_Printf    : Output debug messages to debug log if DEBUG_TO_FILE is defined,
+//                  : else do nothing
+// Returns          :
+// -----------------+
+
+#ifdef DEBUG_TO_FILE
+FILE *gllogstream;
+#endif
+
+#define VA_DBG_BUF_SIZE 8192
+
+#define VA_DBG_PRINT \
+	static char str[VA_DBG_BUF_SIZE] = ""; \
+	va_list arglist; \
+	va_start(arglist, format); \
+	vsnprintf(str, VA_DBG_BUF_SIZE, format, arglist); \
+	va_end(arglist); \
+
+#define VA_DBG_LOGFILE \
+	if (!gllogstream) \
+		gllogstream = fopen("ogllog.txt", "w"); \
+	fwrite(str, strlen(str), 1, gllogstream);
+
+void GL_DBG_Printf(const char *format, ...)
+{
+	VA_DBG_PRINT
+
+#ifdef HAVE_SDL
+	CONS_Debug(DBG_RENDER, "%s", str);
+#endif
+
+#ifdef DEBUG_TO_FILE
+	VA_DBG_LOGFILE
+#endif
+}
+
+// -----------------+
+// GL_MSG_Warning   : Raises a warning.
+//                  :
+// Returns          :
+// -----------------+
+
+void GL_MSG_Warning(const char *format, ...)
+{
+	VA_DBG_PRINT
+
+#ifdef HAVE_SDL
+	CONS_Alert(CONS_WARNING, "%s", str);
+#endif
+
+#ifdef DEBUG_TO_FILE
+	VA_DBG_LOGFILE
+#endif
+}
+
+// -----------------+
+// GL_MSG_Error     : Raises an error.
+//                  :
+// Returns          :
+// -----------------+
+
+void GL_MSG_Error(const char *format, ...)
+{
+	VA_DBG_PRINT
+
+#ifdef HAVE_SDL
+	CONS_Alert(CONS_ERROR, "%s", str);
+#endif
+
+#ifdef DEBUG_TO_FILE
+	VA_DBG_LOGFILE
+#endif
+}
+
+#undef VA_DBG_PRINT
+#undef VA_DBG_LOGFILE
+#undef VA_DBG_BUF_SIZE
diff --git a/src/hardware/r_glcommon/r_glcommon.h b/src/hardware/r_glcommon/r_glcommon.h
new file mode 100644
index 0000000000000000000000000000000000000000..3900b79055ef188ecaa4ad58546a9aa35108371a
--- /dev/null
+++ b/src/hardware/r_glcommon/r_glcommon.h
@@ -0,0 +1,561 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2020 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file r_glcommon.h
+/// \brief Common OpenGL functions and structs shared by OpenGL backends
+
+#ifndef _R_GLCOMMON_H_
+#define _R_GLCOMMON_H_
+
+#define GL_GLEXT_PROTOTYPES
+
+#if defined(HAVE_GLES2)
+	#include <GLES2/gl2.h>
+	#include <GLES2/gl2ext.h>
+#elif defined(HAVE_GLES)
+	#include <GLES/gl.h>
+	#include <GLES/glext.h>
+#else
+	#ifdef HAVE_SDL
+		#define _MATH_DEFINES_DEFINED
+
+		#ifdef _MSC_VER
+		#pragma warning(disable : 4214 4244)
+		#endif
+
+		#include "SDL_opengl.h" //Alam_GBC: Simple, yes?
+
+		#ifdef _MSC_VER
+		#pragma warning(default : 4214 4244)
+		#endif
+	#else
+		#include <GL/gl.h>
+		#include <GL/glu.h>
+		#if defined(STATIC_OPENGL)
+			#include <GL/glext.h>
+		#endif
+	#endif
+#endif
+
+// For GLExtension_Available
+#ifdef HAVE_SDL
+#define _MATH_DEFINES_DEFINED
+#include "SDL.h"
+#endif
+
+#include "../../doomdata.h"
+#include "../../doomtype.h"
+#include "../../doomdef.h"
+
+#include "../../hardware/hw_data.h"  // HWRTexture_s
+#include "../../hardware/hw_defs.h"  // FTextureInfo
+#include "../../hardware/hw_model.h" // model_t / mesh_t / mdlframe_t
+#include "../r_opengl/r_vbo.h"
+
+void GL_DBG_Printf(const char *format, ...);
+void GL_MSG_Warning(const char *format, ...);
+void GL_MSG_Error(const char *format, ...);
+
+#ifdef DEBUG_TO_FILE
+extern FILE *gllogstream;
+#endif
+
+#ifndef R_GL_APIENTRY
+	#if defined(_WIN32)
+		#define R_GL_APIENTRY APIENTRY
+	#else
+		#define R_GL_APIENTRY
+	#endif
+#endif
+
+// ==========================================================================
+//                                                                     PROTOS
+// ==========================================================================
+
+#ifdef STATIC_OPENGL
+/* Miscellaneous */
+#define pglClear glClear
+#define pglGetFloatv glGetFloatv
+#define pglGetIntegerv glGetIntegerv
+#define pglGetString glGetString
+#define pglClearColor glClearColor
+#define pglColorMask glColorMask
+#define pglAlphaFunc glAlphaFunc
+#define pglBlendFunc glBlendFunc
+#define pglCullFace glCullFace
+#define pglPolygonOffset glPolygonOffset
+#define pglEnable glEnable
+#define pglDisable glDisable
+
+/* Depth buffer */
+#define pglDepthMask glDepthMask
+#define pglDepthRange glDepthRange
+
+/* Transformation */
+#define pglViewport glViewport
+
+/* Raster functions */
+#define pglPixelStorei glPixelStorei
+#define pglReadPixels glReadPixels
+
+/* Texture mapping */
+#define pglTexParameteri glTexParameteri
+#define pglTexImage2D glTexImage2D
+#define pglTexSubImage2D glTexSubImage2D
+
+/* Drawing functions */
+#define pglDrawArrays glDrawArrays
+#define pglDrawElements glDrawElements
+
+/* Texture objects */
+#define pglGenTextures glGenTextures
+#define pglDeleteTextures glDeleteTextures
+#define pglBindTexture glBindTexture
+
+/* Texture mapping */
+#define pglCopyTexImage2D glCopyTexImage2D
+#define pglCopyTexSubImage2D glCopyTexSubImage2D
+
+/* 1.3 functions for multitexturing */
+#define pglActiveTexture glActiveTexture
+#else
+/* Miscellaneous */
+typedef void (R_GL_APIENTRY * PFNglClear) (GLbitfield mask);
+extern PFNglClear pglClear;
+typedef void (R_GL_APIENTRY * PFNglGetFloatv) (GLenum pname, GLfloat *params);
+extern PFNglGetFloatv pglGetFloatv;
+typedef void (R_GL_APIENTRY * PFNglGetIntegerv) (GLenum pname, GLint *params);
+extern PFNglGetIntegerv pglGetIntegerv;
+typedef const GLubyte * (R_GL_APIENTRY * PFNglGetString) (GLenum name);
+extern PFNglGetString pglGetString;
+typedef void (R_GL_APIENTRY * PFNglClearColor) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
+extern PFNglClearColor pglClearColor;
+typedef void (R_GL_APIENTRY * PFNglColorMask) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
+extern PFNglColorMask pglColorMask;
+typedef void (R_GL_APIENTRY * PFNglAlphaFunc) (GLenum func, GLclampf ref);
+extern PFNglAlphaFunc pglAlphaFunc;
+typedef void (R_GL_APIENTRY * PFNglBlendFunc) (GLenum sfactor, GLenum dfactor);
+extern PFNglBlendFunc pglBlendFunc;
+typedef void (R_GL_APIENTRY * PFNglCullFace) (GLenum mode);
+extern PFNglCullFace pglCullFace;
+typedef void (R_GL_APIENTRY * PFNglPolygonOffset) (GLfloat factor, GLfloat units);
+extern PFNglPolygonOffset pglPolygonOffset;
+typedef void (R_GL_APIENTRY * PFNglEnable) (GLenum cap);
+extern PFNglEnable pglEnable;
+typedef void (R_GL_APIENTRY * PFNglDisable) (GLenum cap);
+extern PFNglDisable pglDisable;
+
+/* Depth buffer */
+typedef void (R_GL_APIENTRY * PFNglDepthFunc) (GLenum func);
+extern PFNglDepthFunc pglDepthFunc;
+typedef void (R_GL_APIENTRY * PFNglDepthMask) (GLboolean flag);
+extern PFNglDepthMask pglDepthMask;
+
+/* Transformation */
+typedef void (R_GL_APIENTRY * PFNglViewport) (GLint x, GLint y, GLsizei width, GLsizei height);
+extern PFNglViewport pglViewport;
+
+/* Raster functions */
+typedef void (R_GL_APIENTRY * PFNglPixelStorei) (GLenum pname, GLint param);
+extern PFNglPixelStorei pglPixelStorei;
+typedef void (R_GL_APIENTRY * PFNglReadPixels) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);
+extern PFNglReadPixels pglReadPixels;
+
+/* Texture mapping */
+typedef void (R_GL_APIENTRY * PFNglTexParameteri) (GLenum target, GLenum pname, GLint param);
+extern PFNglTexParameteri pglTexParameteri;
+typedef void (R_GL_APIENTRY * PFNglTexImage2D) (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
+extern PFNglTexImage2D pglTexImage2D;
+typedef void (R_GL_APIENTRY * PFNglTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);
+extern PFNglTexSubImage2D pglTexSubImage2D;
+
+/* Drawing functions */
+typedef void (R_GL_APIENTRY * PFNglDrawArrays) (GLenum mode, GLint first, GLsizei count);
+extern PFNglDrawArrays pglDrawArrays;
+typedef void (R_GL_APIENTRY * PFNglDrawElements) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
+extern PFNglDrawElements pglDrawElements;
+
+/* Texture objects */
+typedef void (R_GL_APIENTRY * PFNglGenTextures) (GLsizei n, const GLuint *textures);
+extern PFNglGenTextures pglGenTextures;
+typedef void (R_GL_APIENTRY * PFNglDeleteTextures) (GLsizei n, const GLuint *textures);
+extern PFNglDeleteTextures pglDeleteTextures;
+typedef void (R_GL_APIENTRY * PFNglBindTexture) (GLenum target, GLuint texture);
+extern PFNglBindTexture pglBindTexture;
+
+/* Texture mapping */
+typedef void (R_GL_APIENTRY * PFNglCopyTexImage2D) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
+extern PFNglCopyTexImage2D pglCopyTexImage2D;
+typedef void (R_GL_APIENTRY * PFNglCopyTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+extern PFNglCopyTexSubImage2D pglCopyTexSubImage2D;
+
+/* 1.3 functions for multitexturing */
+typedef void (R_GL_APIENTRY * PFNglActiveTexture) (GLenum);
+extern PFNglActiveTexture pglActiveTexture;
+#endif
+
+//
+// Multi-texturing
+//
+
+#ifndef HAVE_GLES2
+	#ifdef STATIC_OPENGL
+		#define pglClientActiveTexture glClientActiveTexture
+	#else
+		typedef void (R_GL_APIENTRY * PFNglClientActiveTexture) (GLenum);
+		extern PFNglClientActiveTexture pglClientActiveTexture;
+	#endif
+#endif
+
+//
+// Mipmapping
+//
+
+#ifdef HAVE_GLES
+/* Texture mapping */
+typedef void (R_GL_APIENTRY * PFNglGenerateMipmap) (GLenum target);
+extern PFNglGenerateMipmap pglGenerateMipmap;
+#endif
+
+//
+// Depth functions
+//
+#ifndef HAVE_GLES
+	#ifdef STATIC_OPENGL
+		#define pglClearDepth glClearDepth
+		#define pglDepthFunc glDepthFunc
+	#else
+		typedef void (R_GL_APIENTRY * PFNglClearDepth) (GLclampd depth);
+		extern PFNglClearDepth pglClearDepth;
+		typedef void (R_GL_APIENTRY * PFNglDepthRange) (GLclampd near_val, GLclampd far_val);
+		extern PFNglDepthRange pglDepthRange;
+	#endif
+#else
+	#ifdef STATIC_OPENGL
+		#define pglClearDepthf glClearDepthf
+		#define pglDepthFuncf glDepthFuncf
+	#else
+		typedef void (R_GL_APIENTRY * PFNglClearDepthf) (GLclampf depth);
+		extern PFNglClearDepthf pglClearDepthf;
+		typedef void (R_GL_APIENTRY * PFNglDepthRangef) (GLclampf near_val, GLclampf far_val);
+		extern PFNglDepthRangef pglDepthRangef;
+	#endif
+#endif
+
+//
+// Legacy functions
+//
+
+#ifndef HAVE_GLES2
+#ifdef STATIC_OPENGL
+/* Transformation */
+#define pglMatrixMode glMatrixMode
+#define pglViewport glViewport
+#define pglPushMatrix glPushMatrix
+#define pglPopMatrix glPopMatrix
+#define pglLoadIdentity glLoadIdentity
+#define pglMultMatrixf glMultMatrixf
+#define pglRotatef glRotatef
+#define pglScalef glScalef
+#define pglTranslatef glTranslatef
+
+/* Drawing functions */
+#define pglVertexPointer glVertexPointer
+#define pglNormalPointer glNormalPointer
+#define pglTexCoordPointer glTexCoordPointer
+#define pglColorPointer glColorPointer
+#define pglEnableClientState glEnableClientState
+#define pglDisableClientState glDisableClientState
+
+/* Lighting */
+#define pglShadeModel glShadeModel
+#define pglLightfv glLightfv
+#define pglLightModelfv glLightModelfv
+#define pglMaterialfv glMaterialfv
+
+/* Texture mapping */
+#define pglTexEnvi glTexEnvi
+
+#else // STATIC_OPENGL
+
+typedef void (R_GL_APIENTRY * PFNglMatrixMode) (GLenum mode);
+extern PFNglMatrixMode pglMatrixMode;
+typedef void (R_GL_APIENTRY * PFNglPushMatrix) (void);
+extern PFNglPushMatrix pglPushMatrix;
+typedef void (R_GL_APIENTRY * PFNglPopMatrix) (void);
+extern PFNglPopMatrix pglPopMatrix;
+typedef void (R_GL_APIENTRY * PFNglLoadIdentity) (void);
+extern PFNglLoadIdentity pglLoadIdentity;
+typedef void (R_GL_APIENTRY * PFNglMultMatrixf) (const GLfloat *m);
+extern PFNglMultMatrixf pglMultMatrixf;
+typedef void (R_GL_APIENTRY * PFNglRotatef) (GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
+extern PFNglRotatef pglRotatef;
+typedef void (R_GL_APIENTRY * PFNglScalef) (GLfloat x, GLfloat y, GLfloat z);
+extern PFNglScalef pglScalef;
+typedef void (R_GL_APIENTRY * PFNglTranslatef) (GLfloat x, GLfloat y, GLfloat z);
+extern PFNglTranslatef pglTranslatef;
+
+/* Drawing Functions */
+typedef void (R_GL_APIENTRY * PFNglVertexPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+extern PFNglVertexPointer pglVertexPointer;
+typedef void (R_GL_APIENTRY * PFNglNormalPointer) (GLenum type, GLsizei stride, const GLvoid *pointer);
+extern PFNglNormalPointer pglNormalPointer;
+typedef void (R_GL_APIENTRY * PFNglTexCoordPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+extern PFNglTexCoordPointer pglTexCoordPointer;
+typedef void (R_GL_APIENTRY * PFNglColorPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+extern PFNglColorPointer pglColorPointer;
+typedef void (R_GL_APIENTRY * PFNglEnableClientState) (GLenum cap);
+extern PFNglEnableClientState pglEnableClientState;
+typedef void (R_GL_APIENTRY * PFNglDisableClientState) (GLenum cap);
+extern PFNglDisableClientState pglDisableClientState;
+
+/* Lighting */
+typedef void (R_GL_APIENTRY * PFNglShadeModel) (GLenum mode);
+extern PFNglShadeModel pglShadeModel;
+typedef void (R_GL_APIENTRY * PFNglLightfv) (GLenum light, GLenum pname, GLfloat *params);
+extern PFNglLightfv pglLightfv;
+typedef void (R_GL_APIENTRY * PFNglLightModelfv) (GLenum pname, GLfloat *params);
+extern PFNglLightModelfv pglLightModelfv;
+typedef void (R_GL_APIENTRY * PFNglMaterialfv) (GLint face, GLenum pname, GLfloat *params);
+extern PFNglMaterialfv pglMaterialfv;
+
+/* Texture mapping */
+typedef void (R_GL_APIENTRY * PFNglTexEnvi) (GLenum target, GLenum pname, GLint param);
+extern PFNglTexEnvi pglTexEnvi;
+#endif
+#endif // HAVE_GLES2
+
+// Color
+#ifdef HAVE_GLES
+	#ifdef STATIC_OPENGL
+		#define pglColor4f glColor4f
+	#else
+		typedef void (*PFNglColor4f) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+		extern PFNglColor4f pglColor4f;
+	#endif
+#else
+	#ifdef STATIC_OPENGL
+		#define pglColor4ubv glColor4ubv
+	#else
+		typedef void (R_GL_APIENTRY * PFNglColor4ubv) (const GLubyte *v);
+		extern PFNglColor4ubv pglColor4ubv;
+	#endif
+#endif
+
+/* 1.5 functions for buffers */
+typedef void (R_GL_APIENTRY * PFNglGenBuffers) (GLsizei n, GLuint *buffers);
+extern PFNglGenBuffers pglGenBuffers;
+typedef void (R_GL_APIENTRY * PFNglBindBuffer) (GLenum target, GLuint buffer);
+extern PFNglBindBuffer pglBindBuffer;
+typedef void (R_GL_APIENTRY * PFNglBufferData) (GLenum target, GLsizei size, const GLvoid *data, GLenum usage);
+extern PFNglBufferData pglBufferData;
+typedef void (R_GL_APIENTRY * PFNglDeleteBuffers) (GLsizei n, const GLuint *buffers);
+extern PFNglDeleteBuffers pglDeleteBuffers;
+
+/* 2.0 functions */
+typedef void (R_GL_APIENTRY * PFNglBlendEquation) (GLenum mode);
+extern PFNglBlendEquation pglBlendEquation;
+
+// ==========================================================================
+//                                                                  FUNCTIONS
+// ==========================================================================
+
+void GLModel_GenerateVBOs(model_t *model);
+void GLModel_AllocLerpBuffer(size_t size);
+void GLModel_AllocLerpTinyBuffer(size_t size);
+
+void  GLTexture_Flush(void);
+void  GLTexture_SetFilterMode(INT32 mode);
+void  GLTexture_Delete(HWRTexture_t *pTexInfo);
+INT32 GLTexture_GetMemoryUsage(FTextureInfo *head);
+void  GLTexture_GenerateScreenTexture(GLuint *name);
+
+boolean GLBackend_Init(void);
+boolean GLBackend_InitContext(void);
+boolean GLBackend_LoadLibrary(void);
+boolean GLBackend_LoadFunctions(void);
+boolean GLBackend_LoadContextFunctions(void);
+boolean GLBackend_LoadCommonFunctions(void);
+boolean GLBackend_LoadLegacyFunctions(void);
+void   *GLBackend_GetFunction(const char *proc);
+
+INT32 GLBackend_GetShaderType(INT32 type);
+
+void    GLExtension_Init(void);
+boolean GLExtension_Available(const char *extension);
+
+void SetSurface(INT32 w, INT32 h);
+void SetModelView(GLint w, GLint h);
+void SetStates(void);
+void SetBlendingStates(UINT32 PolyFlags);
+void SetNoTexture(void);
+void SetClamp(GLenum pname);
+
+// ==========================================================================
+//                                                                  CONSTANTS
+// ==========================================================================
+
+#define N_PI_DEMI               (M_PIl/2.0f)
+#define ASPECT_RATIO            (1.0f)
+#define FIELD_OF_VIEW           90.0f
+
+#define FAR_CLIPPING_PLANE      32768.0f // Draw further! Tails 01-21-2001
+
+// shortcut for ((float)1/i)
+#define byte2float(x) (x / 255.0f)
+
+#define NULL_VBO_VERTEX ((FSkyVertex*)NULL)
+#define sky_vbo_x (GLExtension_vertex_buffer_object ? &NULL_VBO_VERTEX->x : &sky->data[0].x)
+#define sky_vbo_u (GLExtension_vertex_buffer_object ? &NULL_VBO_VERTEX->u : &sky->data[0].u)
+#define sky_vbo_r (GLExtension_vertex_buffer_object ? &NULL_VBO_VERTEX->r : &sky->data[0].r)
+
+/* 1.2 Parms */
+/* GL_CLAMP_TO_EDGE_EXT */
+#ifndef GL_CLAMP_TO_EDGE
+#define GL_CLAMP_TO_EDGE 0x812F
+#endif
+#ifndef GL_TEXTURE_MIN_LOD
+#define GL_TEXTURE_MIN_LOD 0x813A
+#endif
+#ifndef GL_TEXTURE_MAX_LOD
+#define GL_TEXTURE_MAX_LOD 0x813B
+#endif
+
+/* 1.3 GL_TEXTUREi */
+#ifndef GL_TEXTURE0
+#define GL_TEXTURE0 0x84C0
+#endif
+#ifndef GL_TEXTURE1
+#define GL_TEXTURE1 0x84C1
+#endif
+
+/* 1.5 Parms */
+#ifndef GL_ARRAY_BUFFER
+#define GL_ARRAY_BUFFER 0x8892
+#endif
+#ifndef GL_STATIC_DRAW
+#define GL_STATIC_DRAW 0x88E4
+#endif
+
+#ifndef GL_STATIC_DRAW
+#define GL_STATIC_DRAW 0x88E4
+#endif
+
+#ifndef GL_ARRAY_BUFFER
+#define GL_ARRAY_BUFFER 0x8892
+#endif
+
+#ifndef GL_FUNC_ADD
+#define GL_FUNC_ADD 0x8006
+#endif
+
+#ifndef GL_FUNC_SUBTRACT
+#define GL_FUNC_SUBTRACT 0x800A
+#endif
+
+#ifndef GL_FUNC_REVERSE_SUBTRACT
+#define GL_FUNC_REVERSE_SUBTRACT 0x800B
+#endif
+
+#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT
+#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
+#endif
+
+#ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
+#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
+#endif
+
+// ==========================================================================
+//                                                                    STRUCTS
+// ==========================================================================
+
+struct GLRGBAFloat
+{
+	GLfloat red;
+	GLfloat green;
+	GLfloat blue;
+	GLfloat alpha;
+};
+typedef struct GLRGBAFloat GLRGBAFloat;
+
+struct FExtensionList
+{
+	const char *name;
+	boolean *extension;
+};
+typedef struct FExtensionList FExtensionList;
+
+// ==========================================================================
+//                                                                    GLOBALS
+// ==========================================================================
+
+extern const GLubyte *GLVersion;
+extern const GLubyte *GLRenderer;
+extern const GLubyte *GLExtensions;
+
+extern GLint GLMajorVersion;
+extern GLint GLMinorVersion;
+
+extern struct GPURenderingAPI GLInterfaceAPI;
+
+extern RGBA_t GPUTexturePalette[256];
+extern GLint  GPUTextureFormat;
+extern GLint  GPUScreenWidth, GPUScreenHeight;
+extern GLbyte GPUScreenDepth;
+extern GLint  GPUMaximumAnisotropy;
+
+// Linked list of all textures.
+extern FTextureInfo *TexCacheTail;
+extern FTextureInfo *TexCacheHead;
+
+extern GLuint CurrentTexture;
+extern GLuint BlankTexture; // With OpenGL 1.1+, the first texture should be 1
+
+extern GLuint ScreenTexture;
+extern GLuint FinalScreenTexture;
+extern GLuint WipeStartTexture;
+extern GLuint WipeEndTexture;
+
+extern UINT32 CurrentPolyFlags;
+
+extern GLboolean MipmappingEnabled;
+extern GLboolean ModelLightingEnabled;
+
+extern GLint MipmapMinFilter;
+extern GLint MipmapMagFilter;
+extern GLint AnisotropicFilter;
+
+extern float NearClippingPlane;
+
+extern float *vertBuffer;
+extern float *normBuffer;
+extern short *vertTinyBuffer;
+extern char *normTinyBuffer;
+
+#ifdef HAVE_GLES2
+typedef float fvector3_t[3];
+typedef float fvector4_t[4];
+typedef fvector4_t fmatrix4_t[4];
+
+extern fmatrix4_t projMatrix;
+extern fmatrix4_t viewMatrix;
+extern fmatrix4_t modelMatrix;
+#endif
+
+// ==========================================================================
+//                                                                 EXTENSIONS
+// ==========================================================================
+
+extern boolean GLExtension_multitexture;
+extern boolean GLExtension_vertex_buffer_object;
+extern boolean GLExtension_texture_filter_anisotropic;
+extern boolean GLExtension_vertex_program;
+extern boolean GLExtension_fragment_program;
+extern boolean GLExtension_shaders;
+
+#endif // _R_GLCOMMON_H_
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index 4275d7daee22ce7359a7724533464ee304a27429..99ffc64c5e414b38eacdb40709358b587776ae20 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -10,7 +10,6 @@
 /// \brief OpenGL API for Sonic Robo Blast 2
 
 #if defined (_WIN32)
-//#define WIN32_LEAN_AND_MEAN
 #define RPC_NO_WINDOWS_H
 #include <windows.h>
 #endif
@@ -24,1069 +23,91 @@
 #include "r_opengl.h"
 #include "r_vbo.h"
 
+#include "../shaders/gl_shaders.h"
+
 #if defined (HWRENDER) && !defined (NOROPENGL)
 
-struct GLRGBAFloat
-{
-	GLfloat red;
-	GLfloat green;
-	GLfloat blue;
-	GLfloat alpha;
-};
-typedef struct GLRGBAFloat GLRGBAFloat;
 static const GLubyte white[4] = { 255, 255, 255, 255 };
 
 // ==========================================================================
-//                                                                  CONSTANTS
-// ==========================================================================
-
-#define      ASPECT_RATIO            (1.0f)  //(320.0f/200.0f)
-#define      FIELD_OF_VIEW           90.0f
-
-#define      FAR_CLIPPING_PLANE      32768.0f // Draw further! Tails 01-21-2001
-static float NEAR_CLIPPING_PLANE =   NZCLIP_PLANE;
-
-#define      N_PI_DEMI               (M_PIl/2.0f) //(1.5707963268f)
-
-// **************************************************************************
 //                                                                    GLOBALS
-// **************************************************************************
-
-RGBA_t GPUTexturePalette[256];
-GLint  GPUTextureFormat;
-GLint  GPUScreenWidth, GPUScreenHeight;
-GLbyte GPUScreenDepth;
-GLint  GPUMaximumAnisotropy = 0;
-
-// Linked list of all textures.
-static FTextureInfo *TexCacheTail = NULL;
-static FTextureInfo *TexCacheHead = NULL;
-
-static GLuint CurrentTexture = 0;
-static GLuint BlankTexture = 0; // With OpenGL 1.1+, the first texture should be 1
-
-static GLuint ScreenTexture = 0;
-static GLuint FinalScreenTexture = 0;
-static GLuint WipeStartTexture = 0;
-static GLuint WipeEndTexture = 0;
-
-static UINT32 CurrentPolyFlags;
-
-static GLboolean MipmappingEnabled = GL_FALSE;
-static GLboolean ModelLightingEnabled = GL_FALSE;
-
-static GLint MipmapMinFilter = GL_LINEAR;
-static GLint MipmapMagFilter = GL_LINEAR;
-static GLint AnisotropicFilter = 0;
-
-const GLubyte *GLVersion = NULL;
-const GLubyte *GLRenderer = NULL;
-const GLubyte *GLExtensions = NULL;
+// ==========================================================================
 
 // Hurdler: 04/10/2000: added for the kick ass coronas as Boris wanted;-)
 static GLfloat ModelMatrix[16];
 static GLfloat ProjectionMatrix[16];
 static GLint   SceneViewport[4];
 
-// shortcut for ((float)1/i)
-#define byte2float(x) (x / 255.0f)
-
-// -----------------+
-// GL_DBG_Printf    : Output debug messages to debug log if DEBUG_TO_FILE is defined,
-//                  : else do nothing
-// Returns          :
-// -----------------+
-
-#ifdef DEBUG_TO_FILE
-FILE *gllogstream;
-#endif
-
-FUNCPRINTF void GL_DBG_Printf(const char *format, ...)
-{
-#ifdef DEBUG_TO_FILE
-	char str[4096] = "";
-	va_list arglist;
-
-	if (!gllogstream)
-		gllogstream = fopen("ogllog.txt", "w");
-
-	va_start(arglist, format);
-	vsnprintf(str, 4096, format, arglist);
-	va_end(arglist);
-
-	fwrite(str, strlen(str), 1, gllogstream);
-#else
-	(void)format;
-#endif
-}
-
-// -----------------+
-// GL_MSG_Warning   : Raises a warning.
-//                  :
-// Returns          :
-// -----------------+
-
-static void GL_MSG_Warning(const char *format, ...)
-{
-	char str[4096] = "";
-	va_list arglist;
-
-	va_start(arglist, format);
-	vsnprintf(str, 4096, format, arglist);
-	va_end(arglist);
-
-#ifdef HAVE_SDL
-	CONS_Alert(CONS_WARNING, "%s", str);
-#endif
-#ifdef DEBUG_TO_FILE
-	if (!gllogstream)
-		gllogstream = fopen("ogllog.txt", "w");
-	fwrite(str, strlen(str), 1, gllogstream);
-#endif
-}
-
-// -----------------+
-// GL_MSG_Error     : Raises an error.
-//                  :
-// Returns          :
-// -----------------+
-
-static void GL_MSG_Error(const char *format, ...)
-{
-	char str[4096] = "";
-	va_list arglist;
-
-	va_start(arglist, format);
-	vsnprintf(str, 4096, format, arglist);
-	va_end(arglist);
-
-#ifdef HAVE_SDL
-	CONS_Alert(CONS_ERROR, "%s", str);
-#endif
-#ifdef DEBUG_TO_FILE
-	if (!gllogstream)
-		gllogstream = fopen("ogllog.txt", "w");
-	fwrite(str, strlen(str), 1, gllogstream);
-#endif
-}
-
-#ifdef STATIC_OPENGL
-/* 1.0 functions */
-/* Miscellaneous */
-#define pglClearColor glClearColor
-//glClear
-#define pglColorMask glColorMask
-#define pglAlphaFunc glAlphaFunc
-#define pglBlendFunc glBlendFunc
-#define pglCullFace glCullFace
-#define pglPolygonOffset glPolygonOffset
-#define pglScissor glScissor
-#define pglEnable glEnable
-#define pglDisable glDisable
-#define pglGetFloatv glGetFloatv
-//glGetIntegerv
-//glGetString
-#define pglHint glHint
-
-/* Depth Buffer */
-#define pglClearDepth glClearDepth
-#define pglDepthFunc glDepthFunc
-#define pglDepthMask glDepthMask
-#define pglDepthRange glDepthRange
-
-/* Transformation */
-#define pglMatrixMode glMatrixMode
-#define pglViewport glViewport
-#define pglPushMatrix glPushMatrix
-#define pglPopMatrix glPopMatrix
-#define pglLoadIdentity glLoadIdentity
-#define pglMultMatrixf glMultMatrixf
-#define pglRotatef glRotatef
-#define pglScalef glScalef
-#define pglTranslatef glTranslatef
-
-/* Drawing Functions */
-#define pglColor4ubv glColor4ubv
-#define pglVertexPointer glVertexPointer
-#define pglNormalPointer glNormalPointer
-#define pglTexCoordPointer glTexCoordPointer
-#define pglColorPointer glColorPointer
-#define pglDrawArrays glDrawArrays
-#define pglDrawElements glDrawElements
-#define pglEnableClientState glEnableClientState
-#define pglDisableClientState glDisableClientState
-
-/* Lighting */
-#define pglShadeModel glShadeModel
-#define pglLightfv glLightfv
-#define pglLightModelfv glLightModelfv
-#define pglMaterialfv glMaterialfv
-#define pglMateriali glMateriali
-
-/* Raster functions */
-#define pglPixelStorei glPixelStorei
-#define pglReadPixels glReadPixels
-
-/* Texture mapping */
-#define pglTexEnvi glTexEnvi
-#define pglTexParameteri glTexParameteri
-#define pglTexImage2D glTexImage2D
-#define pglTexSubImage2D glTexSubImage2D
-
-/* 1.1 functions */
-/* texture objects */ //GL_EXT_texture_object
-#define pglGenTextures glGenTextures
-#define pglDeleteTextures glDeleteTextures
-#define pglBindTexture glBindTexture
-/* texture mapping */ //GL_EXT_copy_texture
-#define pglCopyTexImage2D glCopyTexImage2D
-#define pglCopyTexSubImage2D glCopyTexSubImage2D
-
-#else //!STATIC_OPENGL
-
-/* 1.0 functions */
-/* Miscellaneous */
-typedef void (APIENTRY * PFNglClearColor) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
-static PFNglClearColor pglClearColor;
-typedef void (APIENTRY * PFNglColorMask) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
-static PFNglColorMask pglColorMask;
-typedef void (APIENTRY * PFNglAlphaFunc) (GLenum func, GLclampf ref);
-static PFNglAlphaFunc pglAlphaFunc;
-typedef void (APIENTRY * PFNglBlendFunc) (GLenum sfactor, GLenum dfactor);
-static PFNglBlendFunc pglBlendFunc;
-typedef void (APIENTRY * PFNglCullFace) (GLenum mode);
-static PFNglCullFace pglCullFace;
-typedef void (APIENTRY * PFNglPolygonOffset) (GLfloat factor, GLfloat units);
-static PFNglPolygonOffset pglPolygonOffset;
-typedef void (APIENTRY * PFNglScissor) (GLint x, GLint y, GLsizei width, GLsizei height);
-static PFNglScissor pglScissor;
-typedef void (APIENTRY * PFNglEnable) (GLenum cap);
-static PFNglEnable pglEnable;
-typedef void (APIENTRY * PFNglDisable) (GLenum cap);
-static PFNglDisable pglDisable;
-typedef void (APIENTRY * PFNglGetFloatv) (GLenum pname, GLfloat *params);
-static PFNglGetFloatv pglGetFloatv;
-
-/* Depth Buffer */
-typedef void (APIENTRY * PFNglClearDepth) (GLclampd depth);
-static PFNglClearDepth pglClearDepth;
-typedef void (APIENTRY * PFNglDepthFunc) (GLenum func);
-static PFNglDepthFunc pglDepthFunc;
-typedef void (APIENTRY * PFNglDepthMask) (GLboolean flag);
-static PFNglDepthMask pglDepthMask;
-typedef void (APIENTRY * PFNglDepthRange) (GLclampd near_val, GLclampd far_val);
-static PFNglDepthRange pglDepthRange;
-
-/* Transformation */
-typedef void (APIENTRY * PFNglMatrixMode) (GLenum mode);
-static PFNglMatrixMode pglMatrixMode;
-typedef void (APIENTRY * PFNglViewport) (GLint x, GLint y, GLsizei width, GLsizei height);
-static PFNglViewport pglViewport;
-typedef void (APIENTRY * PFNglPushMatrix) (void);
-static PFNglPushMatrix pglPushMatrix;
-typedef void (APIENTRY * PFNglPopMatrix) (void);
-static PFNglPopMatrix pglPopMatrix;
-typedef void (APIENTRY * PFNglLoadIdentity) (void);
-static PFNglLoadIdentity pglLoadIdentity;
-typedef void (APIENTRY * PFNglMultMatrixf) (const GLfloat *m);
-static PFNglMultMatrixf pglMultMatrixf;
-typedef void (APIENTRY * PFNglRotatef) (GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
-static PFNglRotatef pglRotatef;
-typedef void (APIENTRY * PFNglScalef) (GLfloat x, GLfloat y, GLfloat z);
-static PFNglScalef pglScalef;
-typedef void (APIENTRY * PFNglTranslatef) (GLfloat x, GLfloat y, GLfloat z);
-static PFNglTranslatef pglTranslatef;
-
-/* Drawing Functions */
-typedef void (APIENTRY * PFNglColor4ubv) (const GLubyte *v);
-static PFNglColor4ubv pglColor4ubv;
-typedef void (APIENTRY * PFNglVertexPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
-static PFNglVertexPointer pglVertexPointer;
-typedef void (APIENTRY * PFNglNormalPointer) (GLenum type, GLsizei stride, const GLvoid *pointer);
-static PFNglNormalPointer pglNormalPointer;
-typedef void (APIENTRY * PFNglTexCoordPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
-static PFNglTexCoordPointer pglTexCoordPointer;
-typedef void (APIENTRY * PFNglColorPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
-static PFNglColorPointer pglColorPointer;
-typedef void (APIENTRY * PFNglDrawArrays) (GLenum mode, GLint first, GLsizei count);
-static PFNglDrawArrays pglDrawArrays;
-typedef void (APIENTRY * PFNglDrawElements) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
-static PFNglDrawElements pglDrawElements;
-typedef void (APIENTRY * PFNglEnableClientState) (GLenum cap);
-static PFNglEnableClientState pglEnableClientState;
-typedef void (APIENTRY * PFNglDisableClientState) (GLenum cap);
-static PFNglDisableClientState pglDisableClientState;
-
-/* Lighting */
-typedef void (APIENTRY * PFNglShadeModel) (GLenum mode);
-static PFNglShadeModel pglShadeModel;
-typedef void (APIENTRY * PFNglLightfv) (GLenum light, GLenum pname, GLfloat *params);
-static PFNglLightfv pglLightfv;
-typedef void (APIENTRY * PFNglLightModelfv) (GLenum pname, GLfloat *params);
-static PFNglLightModelfv pglLightModelfv;
-typedef void (APIENTRY * PFNglMaterialfv) (GLint face, GLenum pname, GLfloat *params);
-static PFNglMaterialfv pglMaterialfv;
-typedef void (APIENTRY * PFNglMateriali) (GLint face, GLenum pname, GLint param);
-static PFNglMateriali pglMateriali;
-
-/* Raster functions */
-typedef void (APIENTRY * PFNglPixelStorei) (GLenum pname, GLint param);
-static PFNglPixelStorei pglPixelStorei;
-typedef void (APIENTRY  * PFNglReadPixels) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);
-static PFNglReadPixels pglReadPixels;
-
-/* Texture mapping */
-typedef void (APIENTRY * PFNglTexEnvi) (GLenum target, GLenum pname, GLint param);
-static PFNglTexEnvi pglTexEnvi;
-typedef void (APIENTRY * PFNglTexParameteri) (GLenum target, GLenum pname, GLint param);
-static PFNglTexParameteri pglTexParameteri;
-typedef void (APIENTRY * PFNglTexImage2D) (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
-static PFNglTexImage2D pglTexImage2D;
-typedef void (APIENTRY * PFNglTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);
-static PFNglTexSubImage2D pglTexSubImage2D;
-
-/* 1.1 functions */
-/* texture objects */ //GL_EXT_texture_object
-typedef void (APIENTRY * PFNglGenTextures) (GLsizei n, const GLuint *textures);
-static PFNglGenTextures pglGenTextures;
-typedef void (APIENTRY * PFNglDeleteTextures) (GLsizei n, const GLuint *textures);
-static PFNglDeleteTextures pglDeleteTextures;
-typedef void (APIENTRY * PFNglBindTexture) (GLenum target, GLuint texture);
-static PFNglBindTexture pglBindTexture;
-/* texture mapping */ //GL_EXT_copy_texture
-typedef void (APIENTRY * PFNglCopyTexImage2D) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
-static PFNglCopyTexImage2D pglCopyTexImage2D;
-typedef void (APIENTRY * PFNglCopyTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
-static PFNglCopyTexSubImage2D pglCopyTexSubImage2D;
-#endif
-/* GLU functions */
+//* GLU functions */
 typedef GLint (APIENTRY * PFNgluBuild2DMipmaps) (GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *data);
 static PFNgluBuild2DMipmaps pgluBuild2DMipmaps;
 
-/* 1.3 functions for multitexturing */
-typedef void (APIENTRY *PFNglActiveTexture) (GLenum);
-static PFNglActiveTexture pglActiveTexture;
-typedef void (APIENTRY *PFNglMultiTexCoord2f) (GLenum, GLfloat, GLfloat);
-static PFNglMultiTexCoord2f pglMultiTexCoord2f;
-typedef void (APIENTRY *PFNglMultiTexCoord2fv) (GLenum target, const GLfloat *v);
-static PFNglMultiTexCoord2fv pglMultiTexCoord2fv;
-typedef void (APIENTRY *PFNglClientActiveTexture) (GLenum);
-static PFNglClientActiveTexture pglClientActiveTexture;
-
-/* 1.5 functions for buffers */
-typedef void (APIENTRY * PFNglGenBuffers) (GLsizei n, GLuint *buffers);
-static PFNglGenBuffers pglGenBuffers;
-typedef void (APIENTRY * PFNglBindBuffer) (GLenum target, GLuint buffer);
-static PFNglBindBuffer pglBindBuffer;
-typedef void (APIENTRY * PFNglBufferData) (GLenum target, GLsizei size, const GLvoid *data, GLenum usage);
-static PFNglBufferData pglBufferData;
-typedef void (APIENTRY * PFNglDeleteBuffers) (GLsizei n, const GLuint *buffers);
-static PFNglDeleteBuffers pglDeleteBuffers;
-
-/* 2.0 functions */
-typedef void (APIENTRY * PFNglBlendEquation) (GLenum mode);
-static PFNglBlendEquation pglBlendEquation;
-
-
-/* 1.2 Parms */
-/* GL_CLAMP_TO_EDGE_EXT */
-#ifndef GL_CLAMP_TO_EDGE
-#define GL_CLAMP_TO_EDGE 0x812F
-#endif
-#ifndef GL_TEXTURE_MIN_LOD
-#define GL_TEXTURE_MIN_LOD 0x813A
-#endif
-#ifndef GL_TEXTURE_MAX_LOD
-#define GL_TEXTURE_MAX_LOD 0x813B
-#endif
-
-/* 1.3 GL_TEXTUREi */
-#ifndef GL_TEXTURE0
-#define GL_TEXTURE0 0x84C0
-#endif
-#ifndef GL_TEXTURE1
-#define GL_TEXTURE1 0x84C1
-#endif
-
-/* 1.5 Parms */
-#ifndef GL_ARRAY_BUFFER
-#define GL_ARRAY_BUFFER 0x8892
-#endif
-#ifndef GL_STATIC_DRAW
-#define GL_STATIC_DRAW 0x88E4
-#endif
-
-boolean SetupGLfunc(void)
+boolean GLBackend_LoadFunctions(void)
 {
 #ifndef STATIC_OPENGL
-#define GETOPENGLFUNC(func, proc) \
-	func = GetGLFunc(#proc); \
-	if (!func) \
+#define GETOPENGLFUNC(func) \
+	p ## gl ## func = GLBackend_GetFunction("gl" #func); \
+	if (!(p ## gl ## func)) \
 	{ \
-		GL_MSG_Warning("failed to get OpenGL function: %s", #proc); \
+		GL_MSG_Error("failed to get OpenGL function: %s", #func); \
+		return false; \
 	} \
 
-	GETOPENGLFUNC(pglClearColor, glClearColor)
-
-	GETOPENGLFUNC(pglClear, glClear)
-	GETOPENGLFUNC(pglColorMask, glColorMask)
-	GETOPENGLFUNC(pglAlphaFunc, glAlphaFunc)
-	GETOPENGLFUNC(pglBlendFunc, glBlendFunc)
-	GETOPENGLFUNC(pglCullFace, glCullFace)
-	GETOPENGLFUNC(pglPolygonOffset, glPolygonOffset)
-	GETOPENGLFUNC(pglScissor, glScissor)
-	GETOPENGLFUNC(pglEnable, glEnable)
-	GETOPENGLFUNC(pglDisable, glDisable)
-	GETOPENGLFUNC(pglGetFloatv, glGetFloatv)
-	GETOPENGLFUNC(pglGetIntegerv, glGetIntegerv)
-	GETOPENGLFUNC(pglGetString, glGetString)
-
-	GETOPENGLFUNC(pglClearDepth, glClearDepth)
-	GETOPENGLFUNC(pglDepthFunc, glDepthFunc)
-	GETOPENGLFUNC(pglDepthMask, glDepthMask)
-	GETOPENGLFUNC(pglDepthRange, glDepthRange)
-
-	GETOPENGLFUNC(pglMatrixMode, glMatrixMode)
-	GETOPENGLFUNC(pglViewport, glViewport)
-	GETOPENGLFUNC(pglPushMatrix, glPushMatrix)
-	GETOPENGLFUNC(pglPopMatrix, glPopMatrix)
-	GETOPENGLFUNC(pglLoadIdentity, glLoadIdentity)
-	GETOPENGLFUNC(pglMultMatrixf, glMultMatrixf)
-	GETOPENGLFUNC(pglRotatef, glRotatef)
-	GETOPENGLFUNC(pglScalef, glScalef)
-	GETOPENGLFUNC(pglTranslatef, glTranslatef)
-
-	GETOPENGLFUNC(pglColor4ubv, glColor4ubv)
-
-	GETOPENGLFUNC(pglVertexPointer, glVertexPointer)
-	GETOPENGLFUNC(pglNormalPointer, glNormalPointer)
-	GETOPENGLFUNC(pglTexCoordPointer, glTexCoordPointer)
-	GETOPENGLFUNC(pglColorPointer, glColorPointer)
-	GETOPENGLFUNC(pglDrawArrays, glDrawArrays)
-	GETOPENGLFUNC(pglDrawElements, glDrawElements)
-	GETOPENGLFUNC(pglEnableClientState, glEnableClientState)
-	GETOPENGLFUNC(pglDisableClientState, glDisableClientState)
-
-	GETOPENGLFUNC(pglShadeModel, glShadeModel)
-	GETOPENGLFUNC(pglLightfv, glLightfv)
-	GETOPENGLFUNC(pglLightModelfv, glLightModelfv)
-	GETOPENGLFUNC(pglMaterialfv, glMaterialfv)
-	GETOPENGLFUNC(pglMateriali, glMateriali)
-
-	GETOPENGLFUNC(pglPixelStorei, glPixelStorei)
-	GETOPENGLFUNC(pglReadPixels, glReadPixels)
-
-	GETOPENGLFUNC(pglTexEnvi, glTexEnvi)
-	GETOPENGLFUNC(pglTexParameteri, glTexParameteri)
-	GETOPENGLFUNC(pglTexImage2D, glTexImage2D)
-	GETOPENGLFUNC(pglTexSubImage2D, glTexSubImage2D)
-
-	GETOPENGLFUNC(pglGenTextures, glGenTextures)
-	GETOPENGLFUNC(pglDeleteTextures, glDeleteTextures)
-	GETOPENGLFUNC(pglBindTexture, glBindTexture)
-
-	GETOPENGLFUNC(pglCopyTexImage2D, glCopyTexImage2D)
-	GETOPENGLFUNC(pglCopyTexSubImage2D, glCopyTexSubImage2D)
-
-#undef GETOPENGLFUNC
-
-#endif
-	return true;
-}
-
-static GLboolean ShadersEnabled = GL_FALSE;
-static INT32 ShadersAllowed = GPU_SHADEROPTION_OFF;
-
-#ifdef GL_SHADERS
-typedef GLuint 	(APIENTRY *PFNglCreateShader)		(GLenum);
-typedef void 	(APIENTRY *PFNglShaderSource)		(GLuint, GLsizei, const GLchar**, GLint*);
-typedef void 	(APIENTRY *PFNglCompileShader)		(GLuint);
-typedef void 	(APIENTRY *PFNglGetShaderiv)		(GLuint, GLenum, GLint*);
-typedef void 	(APIENTRY *PFNglGetShaderInfoLog)	(GLuint, GLsizei, GLsizei*, GLchar*);
-typedef void 	(APIENTRY *PFNglDeleteShader)		(GLuint);
-typedef GLuint 	(APIENTRY *PFNglCreateProgram)		(void);
-typedef void  	(APIENTRY *PFNglDeleteProgram)		(GLuint);
-typedef void 	(APIENTRY *PFNglAttachShader)		(GLuint, GLuint);
-typedef void 	(APIENTRY *PFNglLinkProgram)		(GLuint);
-typedef void 	(APIENTRY *PFNglGetProgramiv)		(GLuint, GLenum, GLint*);
-typedef void 	(APIENTRY *PFNglUseProgram)			(GLuint);
-typedef void 	(APIENTRY *PFNglUniform1i)			(GLint, GLint);
-typedef void 	(APIENTRY *PFNglUniform1f)			(GLint, GLfloat);
-typedef void 	(APIENTRY *PFNglUniform2f)			(GLint, GLfloat, GLfloat);
-typedef void 	(APIENTRY *PFNglUniform3f)			(GLint, GLfloat, GLfloat, GLfloat);
-typedef void 	(APIENTRY *PFNglUniform4f)			(GLint, GLfloat, GLfloat, GLfloat, GLfloat);
-typedef void 	(APIENTRY *PFNglUniform1fv)			(GLint, GLsizei, const GLfloat*);
-typedef void 	(APIENTRY *PFNglUniform2fv)			(GLint, GLsizei, const GLfloat*);
-typedef void 	(APIENTRY *PFNglUniform3fv)			(GLint, GLsizei, const GLfloat*);
-typedef GLint 	(APIENTRY *PFNglGetUniformLocation)	(GLuint, const GLchar*);
-
-static PFNglCreateShader pglCreateShader;
-static PFNglShaderSource pglShaderSource;
-static PFNglCompileShader pglCompileShader;
-static PFNglGetShaderiv pglGetShaderiv;
-static PFNglGetShaderInfoLog pglGetShaderInfoLog;
-static PFNglDeleteShader pglDeleteShader;
-static PFNglCreateProgram pglCreateProgram;
-static PFNglDeleteProgram pglDeleteProgram;
-static PFNglAttachShader pglAttachShader;
-static PFNglLinkProgram pglLinkProgram;
-static PFNglGetProgramiv pglGetProgramiv;
-static PFNglUseProgram pglUseProgram;
-static PFNglUniform1i pglUniform1i;
-static PFNglUniform1f pglUniform1f;
-static PFNglUniform2f pglUniform2f;
-static PFNglUniform3f pglUniform3f;
-static PFNglUniform4f pglUniform4f;
-static PFNglUniform1fv pglUniform1fv;
-static PFNglUniform2fv pglUniform2fv;
-static PFNglUniform3fv pglUniform3fv;
-static PFNglGetUniformLocation pglGetUniformLocation;
-
-enum EShaderUniform
-{
-	// lighting
-	uniform_poly_color,
-	uniform_tint_color,
-	uniform_fade_color,
-	uniform_lighting,
-	uniform_fade_start,
-	uniform_fade_end,
-
-	// misc. (custom shaders)
-	uniform_leveltime,
-
-	uniform_max,
-};
-
-typedef struct FShaderObject
-{
-	GLuint program;
-	GLint uniforms[uniform_max+1];
-	boolean custom;
-} FShaderObject;
-
-static FShaderObject ShaderObjects[HWR_MAXSHADERS];
-static FShaderObject UserShaderObjects[HWR_MAXSHADERS];
-static FShaderSource CustomShaders[HWR_MAXSHADERS];
-
-// 09102020
-typedef struct FShaderState
-{
-	FShaderObject *current;
-	GLuint type;
-	GLuint program;
-	boolean changed;
-} FShaderState;
-static FShaderState ShaderState;
-
-// Shader info
-static INT32 ShaderLevelTime = 0;
-
-// Lactozilla: Shader functions
-static boolean Shader_CompileProgram(FShaderObject *shader, GLint i, const GLchar *vert_shader, const GLchar *frag_shader);
-static void Shader_CompileError(const char *message, GLuint program, INT32 shadernum);
-static void Shader_SetUniforms(FSurfaceInfo *Surface, GLRGBAFloat *poly, GLRGBAFloat *tint, GLRGBAFloat *fade);
-
-static GLRGBAFloat ShaderDefaultColor = {1.0f, 1.0f, 1.0f, 1.0f};
-
-// ================
-//  Vertex shaders
-// ================
-
-//
-// Generic vertex shader
-//
-
-#define GLSL_DEFAULT_VERTEX_SHADER \
-	"void main()\n" \
-	"{\n" \
-		"gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \
-		"gl_FrontColor = gl_Color;\n" \
-		"gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;\n" \
-		"gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" \
-	"}\0"
-
-// replicates the way fixed function lighting is used by the model lighting option,
-// stores the lighting result to gl_Color
-// (ambient lighting of 0.75 and diffuse lighting from above)
-#define GLSL_MODEL_LIGHTING_VERTEX_SHADER \
-	"void main()\n" \
-	"{\n" \
-		"float nDotVP = dot(gl_Normal, vec3(0, 1, 0));\n" \
-		"float light = 0.75 + max(nDotVP, 0.0);\n" \
-		"gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \
-		"gl_FrontColor = vec4(light, light, light, 1.0);\n" \
-		"gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;\n" \
-		"gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" \
-	"}\0"
-
-// ==================
-//  Fragment shaders
-// ==================
-
-//
-// Generic fragment shader
-//
-
-#define GLSL_DEFAULT_FRAGMENT_SHADER \
-	"uniform sampler2D tex;\n" \
-	"uniform vec4 poly_color;\n" \
-	"void main(void) {\n" \
-		"gl_FragColor = texture2D(tex, gl_TexCoord[0].st) * poly_color;\n" \
-	"}\0"
-
-//
-// Software fragment shader
-//
-
-#define GLSL_DOOM_COLORMAP \
-	"float R_DoomColormap(float light, float z)\n" \
-	"{\n" \
-		"float lightnum = clamp(light / 17.0, 0.0, 15.0);\n" \
-		"float lightz = clamp(z / 16.0, 0.0, 127.0);\n" \
-		"float startmap = (15.0 - lightnum) * 4.0;\n" \
-		"float scale = 160.0 / (lightz + 1.0);\n" \
-		"return startmap - scale * 0.5;\n" \
-	"}\n"
-
-#define GLSL_DOOM_LIGHT_EQUATION \
-	"float R_DoomLightingEquation(float light)\n" \
-	"{\n" \
-		"float z = gl_FragCoord.z / gl_FragCoord.w;\n" \
-		"float colormap = floor(R_DoomColormap(light, z)) + 0.5;\n" \
-		"return clamp(colormap, 0.0, 31.0) / 32.0;\n" \
-	"}\n"
-
-#define GLSL_SOFTWARE_TINT_EQUATION \
-	"if (tint_color.a > 0.0) {\n" \
-		"float color_bright = sqrt((base_color.r * base_color.r) + (base_color.g * base_color.g) + (base_color.b * base_color.b));\n" \
-		"float strength = sqrt(9.0 * tint_color.a);\n" \
-		"final_color.r = clamp((color_bright * (tint_color.r * strength)) + (base_color.r * (1.0 - strength)), 0.0, 1.0);\n" \
-		"final_color.g = clamp((color_bright * (tint_color.g * strength)) + (base_color.g * (1.0 - strength)), 0.0, 1.0);\n" \
-		"final_color.b = clamp((color_bright * (tint_color.b * strength)) + (base_color.b * (1.0 - strength)), 0.0, 1.0);\n" \
-	"}\n"
-
-#define GLSL_SOFTWARE_FADE_EQUATION \
-	"float darkness = R_DoomLightingEquation(lighting);\n" \
-	"if (fade_start != 0.0 || fade_end != 31.0) {\n" \
-		"float fs = fade_start / 31.0;\n" \
-		"float fe = fade_end / 31.0;\n" \
-		"float fd = fe - fs;\n" \
-		"darkness = clamp((darkness - fs) * (1.0 / fd), 0.0, 1.0);\n" \
-	"}\n" \
-	"final_color = mix(final_color, fade_color, darkness);\n"
-
-#define GLSL_SOFTWARE_FRAGMENT_SHADER \
-	"uniform sampler2D tex;\n" \
-	"uniform vec4 poly_color;\n" \
-	"uniform vec4 tint_color;\n" \
-	"uniform vec4 fade_color;\n" \
-	"uniform float lighting;\n" \
-	"uniform float fade_start;\n" \
-	"uniform float fade_end;\n" \
-	GLSL_DOOM_COLORMAP \
-	GLSL_DOOM_LIGHT_EQUATION \
-	"void main(void) {\n" \
-		"vec4 texel = texture2D(tex, gl_TexCoord[0].st);\n" \
-		"vec4 base_color = texel * poly_color;\n" \
-		"vec4 final_color = base_color;\n" \
-		GLSL_SOFTWARE_TINT_EQUATION \
-		GLSL_SOFTWARE_FADE_EQUATION \
-		"final_color.a = texel.a * poly_color.a;\n" \
-		"gl_FragColor = final_color;\n" \
-	"}\0"
-
-// same as above but multiplies results with the lighting value from the
-// accompanying vertex shader (stored in gl_Color)
-#define GLSL_SOFTWARE_MODEL_LIGHTING_FRAGMENT_SHADER \
-	"uniform sampler2D tex;\n" \
-	"uniform vec4 poly_color;\n" \
-	"uniform vec4 tint_color;\n" \
-	"uniform vec4 fade_color;\n" \
-	"uniform float lighting;\n" \
-	"uniform float fade_start;\n" \
-	"uniform float fade_end;\n" \
-	GLSL_DOOM_COLORMAP \
-	GLSL_DOOM_LIGHT_EQUATION \
-	"void main(void) {\n" \
-		"vec4 texel = texture2D(tex, gl_TexCoord[0].st);\n" \
-		"vec4 base_color = texel * poly_color;\n" \
-		"vec4 final_color = base_color;\n" \
-		GLSL_SOFTWARE_TINT_EQUATION \
-		GLSL_SOFTWARE_FADE_EQUATION \
-		"final_color *= gl_Color;\n" \
-		"final_color.a = texel.a * poly_color.a;\n" \
-		"gl_FragColor = final_color;\n" \
-	"}\0"
-
-//
-// Water surface shader
-//
-// Mostly guesstimated, rather than the rest being built off Software science.
-// Still needs to distort things underneath/around the water...
-//
-
-#define GLSL_WATER_FRAGMENT_SHADER \
-	"uniform sampler2D tex;\n" \
-	"uniform vec4 poly_color;\n" \
-	"uniform vec4 tint_color;\n" \
-	"uniform vec4 fade_color;\n" \
-	"uniform float lighting;\n" \
-	"uniform float fade_start;\n" \
-	"uniform float fade_end;\n" \
-	"uniform float leveltime;\n" \
-	"const float freq = 0.025;\n" \
-	"const float amp = 0.025;\n" \
-	"const float speed = 2.0;\n" \
-	"const float pi = 3.14159;\n" \
-	GLSL_DOOM_COLORMAP \
-	GLSL_DOOM_LIGHT_EQUATION \
-	"void main(void) {\n" \
-		"float z = (gl_FragCoord.z / gl_FragCoord.w) / 2.0;\n" \
-		"float a = -pi * (z * freq) + (leveltime * speed);\n" \
-		"float sdistort = sin(a) * amp;\n" \
-		"float cdistort = cos(a) * amp;\n" \
-		"vec4 texel = texture2D(tex, vec2(gl_TexCoord[0].s - sdistort, gl_TexCoord[0].t - cdistort));\n" \
-		"vec4 base_color = texel * poly_color;\n" \
-		"vec4 final_color = base_color;\n" \
-		GLSL_SOFTWARE_TINT_EQUATION \
-		GLSL_SOFTWARE_FADE_EQUATION \
-		"final_color.a = texel.a * poly_color.a;\n" \
-		"gl_FragColor = final_color;\n" \
-	"}\0"
-
-//
-// Fog block shader
-//
-// Alpha of the planes themselves are still slightly off -- see HWR_FogBlockAlpha
-//
-
-#define GLSL_FOG_FRAGMENT_SHADER \
-	"uniform vec4 tint_color;\n" \
-	"uniform vec4 fade_color;\n" \
-	"uniform float lighting;\n" \
-	"uniform float fade_start;\n" \
-	"uniform float fade_end;\n" \
-	GLSL_DOOM_COLORMAP \
-	GLSL_DOOM_LIGHT_EQUATION \
-	"void main(void) {\n" \
-		"vec4 base_color = gl_Color;\n" \
-		"vec4 final_color = base_color;\n" \
-		GLSL_SOFTWARE_TINT_EQUATION \
-		GLSL_SOFTWARE_FADE_EQUATION \
-		"gl_FragColor = final_color;\n" \
-	"}\0"
-
-//
-// Sky fragment shader
-// Modulates poly_color with gl_Color
-//
-#define GLSL_SKY_FRAGMENT_SHADER \
-	"uniform sampler2D tex;\n" \
-	"uniform vec4 poly_color;\n" \
-	"void main(void) {\n" \
-		"gl_FragColor = texture2D(tex, gl_TexCoord[0].st) * gl_Color * poly_color;\n" \
-	"}\0"
-
-// ================
-//  Shader sources
-// ================
-
-static struct {
-	const char *vertex;
-	const char *fragment;
-} const gl_shadersources[] = {
-	// Default shader
-	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_DEFAULT_FRAGMENT_SHADER},
-
-	// Floor shader
-	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER},
-
-	// Wall shader
-	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER},
-
-	// Sprite shader
-	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER},
-
-	// Model shader
-	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER},
-
-	// Model shader + diffuse lighting from above
-	{GLSL_MODEL_LIGHTING_VERTEX_SHADER, GLSL_SOFTWARE_MODEL_LIGHTING_FRAGMENT_SHADER},
-
-	// Water shader
-	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_WATER_FRAGMENT_SHADER},
-
-	// Fog shader
-	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_FOG_FRAGMENT_SHADER},
-
-	// Sky shader
-	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SKY_FRAGMENT_SHADER},
-
-	{NULL, NULL},
-};
-
-#endif	// GL_SHADERS
-
-void SetupGLFunc4(void)
-{
-	pglActiveTexture = GetGLFunc("glActiveTexture");
-	pglMultiTexCoord2f = GetGLFunc("glMultiTexCoord2f");
-	pglClientActiveTexture = GetGLFunc("glClientActiveTexture");
-	pglMultiTexCoord2fv = GetGLFunc("glMultiTexCoord2fv");
-
-	/* 1.5 funcs */
-	pglGenBuffers = GetGLFunc("glGenBuffers");
-	pglBindBuffer = GetGLFunc("glBindBuffer");
-	pglBufferData = GetGLFunc("glBufferData");
-	pglDeleteBuffers = GetGLFunc("glDeleteBuffers");
-
-	/* 2.0 funcs */
-	pglBlendEquation = GetGLFunc("glBlendEquation");
-
-#ifdef GL_SHADERS
-	pglCreateShader = GetGLFunc("glCreateShader");
-	pglShaderSource = GetGLFunc("glShaderSource");
-	pglCompileShader = GetGLFunc("glCompileShader");
-	pglGetShaderiv = GetGLFunc("glGetShaderiv");
-	pglGetShaderInfoLog = GetGLFunc("glGetShaderInfoLog");
-	pglDeleteShader = GetGLFunc("glDeleteShader");
-	pglCreateProgram = GetGLFunc("glCreateProgram");
-	pglDeleteProgram = GetGLFunc("glDeleteProgram");
-	pglAttachShader = GetGLFunc("glAttachShader");
-	pglLinkProgram = GetGLFunc("glLinkProgram");
-	pglGetProgramiv = GetGLFunc("glGetProgramiv");
-	pglUseProgram = GetGLFunc("glUseProgram");
-	pglUniform1i = GetGLFunc("glUniform1i");
-	pglUniform1f = GetGLFunc("glUniform1f");
-	pglUniform2f = GetGLFunc("glUniform2f");
-	pglUniform3f = GetGLFunc("glUniform3f");
-	pglUniform4f = GetGLFunc("glUniform4f");
-	pglUniform1fv = GetGLFunc("glUniform1fv");
-	pglUniform2fv = GetGLFunc("glUniform2fv");
-	pglUniform3fv = GetGLFunc("glUniform3fv");
-	pglGetUniformLocation = GetGLFunc("glGetUniformLocation");
-#endif
-
-	// GLU
-	pgluBuild2DMipmaps = GetGLFunc("gluBuild2DMipmaps");
-}
-
-// jimita
-static boolean CompileShaders(void)
-{
-#ifdef GL_SHADERS
-	GLint i;
-
-	if (!pglUseProgram)
+	if (!GLBackend_LoadCommonFunctions())
 		return false;
 
-	CustomShaders[SHADER_DEFAULT].vertex = NULL;
-	CustomShaders[SHADER_DEFAULT].fragment = NULL;
-
-	for (i = 0; gl_shadersources[i].vertex && gl_shadersources[i].fragment; i++)
-	{
-		FShaderObject *shader, *usershader;
-		const GLchar *vert_shader = gl_shadersources[i].vertex;
-		const GLchar *frag_shader = gl_shadersources[i].fragment;
-
-		if (i >= HWR_MAXSHADERS)
-			break;
-
-		shader = &ShaderObjects[i];
-		usershader = &UserShaderObjects[i];
-
-		if (shader->program)
-			pglDeleteProgram(shader->program);
-		if (usershader->program)
-			pglDeleteProgram(usershader->program);
-
-		shader->program = 0;
-		usershader->program = 0;
+	GETOPENGLFUNC(ClearDepth)
+	GETOPENGLFUNC(DepthRange)
 
-		if (!Shader_CompileProgram(shader, i, vert_shader, frag_shader))
-			shader->program = 0;
+	GETOPENGLFUNC(Color4ubv)
 
-		// Compile custom shader
-		if ((i == SHADER_DEFAULT) || !(CustomShaders[i].vertex || CustomShaders[i].fragment))
-			continue;
+	GETOPENGLFUNC(VertexPointer)
+	GETOPENGLFUNC(NormalPointer)
+	GETOPENGLFUNC(TexCoordPointer)
+	GETOPENGLFUNC(ColorPointer)
+	GETOPENGLFUNC(EnableClientState)
+	GETOPENGLFUNC(DisableClientState)
 
-		// 18032019
-		if (CustomShaders[i].vertex)
-			vert_shader = CustomShaders[i].vertex;
-		if (CustomShaders[i].fragment)
-			frag_shader = CustomShaders[i].fragment;
+	GETOPENGLFUNC(TexEnvi)
 
-		if (!Shader_CompileProgram(usershader, i, vert_shader, frag_shader))
-		{
-			GL_MSG_Warning("CompileShaders: Could not compile custom shader program for %s\n", HWR_GetShaderName(i));
-			usershader->program = 0;
-		}
-	}
-
-	SetShader(SHADER_DEFAULT);
+	if (!GLBackend_LoadLegacyFunctions())
+		return false;
 
-	return true;
-#else
-	return false;
 #endif
+	return true;
 }
 
-//
-// Shader info
-// Those are given to the uniforms.
-//
-
-static void SetShaderInfo(INT32 info, INT32 value)
+boolean GLBackend_LoadContextFunctions(void)
 {
-#ifdef GL_SHADERS
-	switch (info)
+	if (GLExtension_multitexture)
 	{
-		case GPU_SHADERINFO_LEVELTIME:
-			ShaderLevelTime = value;
-			break;
-		default:
-			break;
+		GETOPENGLFUNC(ActiveTexture)
+		GETOPENGLFUNC(ClientActiveTexture)
 	}
-#else
-	(void)info;
-	(void)value;
-#endif
-}
-
-//
-// Custom shader loading
-//
-static void LoadCustomShader(int number, char *code, size_t size, boolean isfragment)
-{
-#ifdef GL_SHADERS
-	FShaderSource *shader;
-
-	if (!pglUseProgram)
-		return;
-
-	if (number < 1 || number > HWR_MAXSHADERS)
-		I_Error("LoadCustomShader: cannot load shader %d (min 1, max %d)", number, HWR_MAXSHADERS);
-	else if (code == NULL)
-		I_Error("LoadCustomShader: empty shader");
-
-	shader = &CustomShaders[number];
 
-#define COPYSHADER(source) { \
-	if (shader->source) \
-		free(shader->source); \
-	shader->source = malloc(size+1); \
-	strncpy(shader->source, code, size); \
-	shader->source[size] = 0; \
+	if (GLExtension_vertex_buffer_object)
+	{
+		GETOPENGLFUNC(GenBuffers)
+		GETOPENGLFUNC(BindBuffer)
+		GETOPENGLFUNC(BufferData)
+		GETOPENGLFUNC(DeleteBuffers)
 	}
 
-	if (isfragment)
-		COPYSHADER(fragment)
-	else
-		COPYSHADER(vertex)
+	if (GLMajorVersion >= 2)
+		GETOPENGLFUNC(BlendEquation)
 
-#else
-	(void)number;
-	(void)shader;
-	(void)size;
-	(void)fragment;
-#endif
-}
-static void SetShader(int type)
-{
 #ifdef GL_SHADERS
-	if (ShadersAllowed != GPU_SHADEROPTION_OFF)
-	{
-		FShaderObject *shader = ShaderState.current;
-
-		// If using model lighting, set the appropriate shader.
-		// However don't override a custom shader.
-		if (type == SHADER_MODEL && ModelLightingEnabled
-		&& !(ShaderObjects[SHADER_MODEL].custom && !ShaderObjects[SHADER_MODEL_LIGHTING].custom))
-			type = SHADER_MODEL_LIGHTING;
-
-		if ((shader == NULL) || (GLuint)type != ShaderState.type)
-		{
-			FShaderObject *baseshader = &ShaderObjects[type];
-			FShaderObject *usershader = &UserShaderObjects[type];
-
-			if (usershader->program)
-				shader = (ShadersAllowed == GPU_SHADEROPTION_NOCUSTOM) ? baseshader : usershader;
-			else
-				shader = baseshader;
-
-			ShaderState.current = shader;
-			ShaderState.type = type;
-			ShaderState.changed = true;
-		}
-
-		if (ShaderState.program != shader->program)
-		{
-			ShaderState.program = shader->program;
-			ShaderState.changed = true;
-		}
-
-		ShadersEnabled = (shader->program != 0) ? GL_TRUE : GL_FALSE;
-		return;
-	}
-#else
-	(void)type;
+	if (GLExtension_shaders)
+		Shader_LoadFunctions();
 #endif
-	ShadersEnabled = GL_FALSE;
-}
-static void UnSetShader(void)
-{
-#ifdef GL_SHADERS
-	ShaderState.current = NULL;
-	ShaderState.type = 0;
-	ShaderState.program = 0;
-
-	if (pglUseProgram)
-		pglUseProgram(0);
-#endif
-
-	ShadersEnabled = GL_FALSE;
-}
-static void CleanShaders(void)
-{
-	INT32 i;
-
-	for (i = 1; i < HWR_MAXSHADERS; i++)
-	{
-		FShaderSource *shader = &CustomShaders[i];
 
-		if (shader->vertex)
-			free(shader->vertex);
-
-		if (shader->fragment)
-			free(shader->fragment);
+	// GLU
+	pgluBuild2DMipmaps = GLBackend_GetFunction("gluBuild2DMipmaps");
 
-		shader->vertex = NULL;
-		shader->fragment = NULL;
-	}
+	return true;
 }
 
-// -----------------+
-// SetNoTexture     : Disable texture
-// -----------------+
-static void SetNoTexture(void)
-{
-	// Disable texture.
-	if (CurrentTexture != BlankTexture)
-	{
-		if (BlankTexture == 0)
-		{
-			// Generate a 1x1 white pixel for the blank texture
-			pglGenTextures(1, &BlankTexture);
-			pglBindTexture(GL_TEXTURE_2D, BlankTexture);
-			pglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, white);
-		}
-		else
-			pglBindTexture(GL_TEXTURE_2D, BlankTexture);
-
-		CurrentTexture = BlankTexture;
-	}
-}
+#undef GETOPENGLFUNC
 
 static void GLPerspective(GLfloat fovy, GLfloat aspect)
 {
@@ -1097,7 +118,7 @@ static void GLPerspective(GLfloat fovy, GLfloat aspect)
 		{ 0.0f, 0.0f, 1.0f,-1.0f},
 		{ 0.0f, 0.0f, 0.0f, 0.0f},
 	};
-	const GLfloat zNear = NEAR_CLIPPING_PLANE;
+	const GLfloat zNear = NearClippingPlane;
 	const GLfloat zFar = FAR_CLIPPING_PLANE;
 	const GLfloat radians = (GLfloat)(fovy / 2.0f * M_PIl / 180.0f);
 	const GLfloat sine = sin(radians);
@@ -1142,225 +163,78 @@ static void GLProject(GLfloat objX, GLfloat objY, GLfloat objZ,
 	}
 	if (fpclassify(in[3]) == FP_ZERO) return;
 	in[0] /= in[3];
-	in[1] /= in[3];
-	in[2] /= in[3];
-	/* Map x, y and z to range 0-1 */
-	in[0] = in[0] * 0.5f + 0.5f;
-	in[1] = in[1] * 0.5f + 0.5f;
-	in[2] = in[2] * 0.5f + 0.5f;
-
-	/* Map x,y to viewport */
-	in[0] = in[0] * SceneViewport[2] + SceneViewport[0];
-	in[1] = in[1] * SceneViewport[3] + SceneViewport[1];
-
-	*winX=in[0];
-	*winY=in[1];
-	*winZ=in[2];
-}
-
-// -----------------+
-// SetModelView     :
-// -----------------+
-void SetModelView(GLint w, GLint h)
-{
-//	GL_DBG_Printf("SetModelView(): %dx%d\n", (int)w, (int)h);
-
-	// The screen textures need to be flushed if the width or height change so that they be remade for the correct size
-	if (GPUScreenWidth != w || GPUScreenHeight != h)
-		FlushScreenTextures();
-
-	GPUScreenWidth = w;
-	GPUScreenHeight = h;
-
-	pglViewport(0, 0, w, h);
-
-	pglMatrixMode(GL_PROJECTION);
-	pglLoadIdentity();
-
-	pglMatrixMode(GL_MODELVIEW);
-	pglLoadIdentity();
-
-	GLPerspective(FIELD_OF_VIEW, ASPECT_RATIO);
-	//pglScalef(1.0f, 320.0f/200.0f, 1.0f);  // gl_scalefrustum (ORIGINAL_ASPECT)
-
-	// added for new coronas' code (without depth buffer)
-	pglGetIntegerv(GL_VIEWPORT, SceneViewport);
-	pglGetFloatv(GL_PROJECTION_MATRIX, ProjectionMatrix);
-}
-
-
-// -----------------+
-// SetStates        : Set permanent states
-// -----------------+
-void SetStates(void)
-{
-#ifdef GL_LIGHT_MODEL_AMBIENT
-	GLfloat LightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
-#endif
-
-//	GL_DBG_Printf("SetStates()\n");
-
-	// Hurdler: not necessary, is it?
-	pglShadeModel(GL_SMOOTH);      // iterate vertice colors
-	//pglShadeModel(GL_FLAT);
-
-	pglEnable(GL_TEXTURE_2D);      // two-dimensional texturing
-
-	pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-
-	pglEnable(GL_ALPHA_TEST);
-	pglAlphaFunc(GL_NOTEQUAL, 0.0f);
-
-	//pglBlendFunc(GL_ONE, GL_ZERO); // copy pixel to frame buffer (opaque)
-	pglEnable(GL_BLEND);           // enable color blending
-
-	pglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-
-	//pglDisable(GL_DITHER);         // faB: ??? (undocumented in OpenGL 1.1)
-	                              // Hurdler: yes, it is!
-	pglEnable(GL_DEPTH_TEST);    // check the depth buffer
-	pglDepthMask(GL_TRUE);             // enable writing to depth buffer
-	pglClearDepth(1.0f);
-	pglDepthRange(0.0f, 1.0f);
-	pglDepthFunc(GL_LEQUAL);
-
-	// this set CurrentPolyFlags to the actual configuration
-	CurrentPolyFlags = 0xffffffff;
-	SetBlend(0);
-
-	CurrentTexture = 0;
-	SetNoTexture();
-
-	pglPolygonOffset(-1.0f, -1.0f);
-
-	//pglEnable(GL_CULL_FACE);
-	//pglCullFace(GL_FRONT);
-
-	pglDisable(GL_FOG);
+	in[1] /= in[3];
+	in[2] /= in[3];
+	/* Map x, y and z to range 0-1 */
+	in[0] = in[0] * 0.5f + 0.5f;
+	in[1] = in[1] * 0.5f + 0.5f;
+	in[2] = in[2] * 0.5f + 0.5f;
 
-	// Lighting for models
-#ifdef GL_LIGHT_MODEL_AMBIENT
-	pglLightModelfv(GL_LIGHT_MODEL_AMBIENT, LightDiffuse);
-	pglEnable(GL_LIGHT0);
-#endif
+	/* Map x,y to viewport */
+	in[0] = in[0] * SceneViewport[2] + SceneViewport[0];
+	in[1] = in[1] * SceneViewport[3] + SceneViewport[1];
 
-	// bp : when no t&l :)
-	pglLoadIdentity();
-	pglScalef(1.0f, 1.0f, -1.0f);
-	pglGetFloatv(GL_MODELVIEW_MATRIX, ModelMatrix); // added for new coronas' code (without depth buffer)
+	*winX=in[0];
+	*winY=in[1];
+	*winZ=in[2];
 }
 
+// ==========================================================================
+//                                                                        API
+// ==========================================================================
 
 // -----------------+
-// DeleteTexture    : Deletes a texture from the GPU and frees its data
+// Init             : Initializes the OpenGL interface API
 // -----------------+
-static void DeleteTexture(HWRTexture_t *pTexInfo)
+static boolean Init(void)
 {
-	FTextureInfo *head = TexCacheHead;
-
-	if (!pTexInfo)
-		return;
-	else if (pTexInfo->downloaded)
-		pglDeleteTextures(1, (GLuint *)&pTexInfo->downloaded);
-
-	while (head)
-	{
-		if (head->name == pTexInfo->downloaded)
-		{
-			if (head->next)
-				head->next->prev = head->prev;
-			if (head->prev)
-				head->prev->next = head->next;
-			free(head);
-			break;
-		}
-
-		head = head->next;
-	}
-
-	pTexInfo->downloaded = 0;
+	return GLBackend_Init();
 }
 
-
 // -----------------+
-// Flush            : flush OpenGL textures
-//                  : Clear list of downloaded mipmaps
+// SetNoTexture     : Disable texture
 // -----------------+
-void Flush(void)
+void SetNoTexture(void)
 {
-	//GL_DBG_Printf ("HWR_Flush()\n");
-
-	while (TexCacheHead)
+	// Disable texture.
+	if (CurrentTexture != BlankTexture)
 	{
-		FTextureInfo *pTexInfo = TexCacheHead;
-		HWRTexture_t *texture = pTexInfo->texture;
-
-		if (pTexInfo->name)
+		if (BlankTexture == 0)
 		{
-			pglDeleteTextures(1, (GLuint *)&pTexInfo->name);
-			pTexInfo->name = 0;
+			// Generate a 1x1 white pixel for the blank texture
+			pglGenTextures(1, &BlankTexture);
+			pglBindTexture(GL_TEXTURE_2D, BlankTexture);
+			pglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, white);
 		}
+		else
+			pglBindTexture(GL_TEXTURE_2D, BlankTexture);
 
-		if (texture)
-			texture->downloaded = 0;
-
-		TexCacheHead = pTexInfo->next;
-		free(pTexInfo);
+		CurrentTexture = BlankTexture;
 	}
-
-	TexCacheTail = TexCacheHead = NULL; // Hurdler: well, TexCacheHead is already NULL
-	CurrentTexture = 0;
 }
 
-
 // -----------------+
-// isExtAvailable   : Look if an OpenGL extension is available
-// Returns          : true if extension available
+// DeleteTexture    : Deletes a texture from the GPU and frees its data
 // -----------------+
-INT32 isExtAvailable(const char *extension, const GLubyte *start)
+static void DeleteTexture(HWRTexture_t *pTexInfo)
 {
-	GLubyte         *where, *terminator;
-
-	if (!extension || !start) return 0;
-	where = (GLubyte *) strchr(extension, ' ');
-	if (where || *extension == '\0')
-		return 0;
-
-	for (;;)
-	{
-		where = (GLubyte *) strstr((const char *) start, extension);
-		if (!where)
-			break;
-		terminator = where + strlen(extension);
-		if (where == start || *(where - 1) == ' ')
-			if (*terminator == ' ' || *terminator == '\0')
-				return 1;
-		start = terminator;
-	}
-	return 0;
-}
-
+	if (!pTexInfo)
+		return;
+	else if (pTexInfo->downloaded)
+		pglDeleteTextures(1, (GLuint *)&pTexInfo->downloaded);
 
-// -----------------+
-// Init             : Initialise the OpenGL interface API
-// Returns          :
-// -----------------+
-static boolean Init(void)
-{
-	return SetupGLfunc();
+	GLTexture_Delete(pTexInfo);
+	pTexInfo->downloaded = 0;
 }
 
-
 // -----------------+
 // ClearTextureCache: Flush OpenGL textures from memory
 // -----------------+
 static void ClearTextureCache(void)
 {
-	// GL_DBG_Printf ("HWR_Flush(exe)\n");
-	Flush();
+	GLTexture_Flush();
 }
 
-
 // -----------------+
 // ReadRect         : Read a rectangle region of the truecolor framebuffer
 //                  : store pixels as 16bit 565 RGB
@@ -1412,7 +286,6 @@ static void ReadRect(INT32 x, INT32 y, INT32 width, INT32 height,
 	}
 }
 
-
 // -----------------+
 // GClipRect        : Defines the 2D hardware clipping window
 // -----------------+
@@ -1421,7 +294,7 @@ static void GClipRect(INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float near
 	// GL_DBG_Printf ("GClipRect(%d, %d, %d, %d)\n", minx, miny, maxx, maxy);
 
 	pglViewport(minx, GPUScreenHeight-maxy, maxx-minx, maxy-miny);
-	NEAR_CLIPPING_PLANE = nearclip;
+	NearClippingPlane = nearclip;
 
 	//pglScissor(minx, GPUScreenHeight-maxy, maxx-minx, maxy-miny);
 	pglMatrixMode(GL_PROJECTION);
@@ -1434,7 +307,6 @@ static void GClipRect(INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float near
 	pglGetFloatv(GL_PROJECTION_MATRIX, ProjectionMatrix);
 }
 
-
 // -----------------+
 // SetPalette       : Changes the current texture palette
 // -----------------+
@@ -1445,10 +317,23 @@ static void SetPalette(RGBA_t *palette)
 	if (memcmp(&GPUTexturePalette, palette, palsize))
 	{
 		memcpy(&GPUTexturePalette, palette, palsize);
-		Flush();
+		GLTexture_Flush();
 	}
 }
 
+void SetClamp(GLenum pname)
+{
+	pglTexParameteri(GL_TEXTURE_2D, pname, GL_CLAMP); // fallback clamp
+	pglTexParameteri(GL_TEXTURE_2D, pname, GL_CLAMP_TO_EDGE);
+}
+
+// -----------------+
+// SetBlend         : Set render mode
+// -----------------+
+static void SetBlend(UINT32 PolyFlags)
+{
+	SetBlendingStates(PolyFlags);
+}
 
 // -----------------+
 // ClearBuffer      : Clear the color/alpha/depth buffer(s)
@@ -1479,9 +364,8 @@ static void ClearBuffer(boolean ColorMask, boolean DepthMask, FRGBAFloat *ClearC
 	pglEnableClientState(GL_TEXTURE_COORD_ARRAY); // And mostly this one, too
 }
 
-
 // -----------------+
-// HWRAPI Draw2DLine: Render a 2D line
+// Draw2DLine       : Render a 2D line
 // -----------------+
 static void Draw2DLine(F2DCoord * v1,
                                    F2DCoord * v2,
@@ -1519,193 +403,9 @@ static void Draw2DLine(F2DCoord * v1,
 	pglEnable(GL_TEXTURE_2D);
 }
 
-
 // -----------------+
-// SetBlend         : Set render mode
+// UpdateTexture    : Updates the texture data.
 // -----------------+
-// PF_Masked - we could use an ALPHA_TEST of GL_EQUAL, and alpha ref of 0,
-//             is it faster when pixels are discarded ?
-
-static void Clamp2D(GLenum pname)
-{
-	pglTexParameteri(GL_TEXTURE_2D, pname, GL_CLAMP); // fallback clamp
-	pglTexParameteri(GL_TEXTURE_2D, pname, GL_CLAMP_TO_EDGE);
-}
-
-static void SetBlendEquation(GLenum mode)
-{
-	if (pglBlendEquation)
-		pglBlendEquation(mode);
-}
-
-static void SetBlendMode(UINT32 flags)
-{
-	// Set blending function
-	switch (flags)
-	{
-		case PF_Translucent & PF_Blending:
-			pglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // alpha = level of transparency
-			break;
-		case PF_Masked & PF_Blending:
-			// Hurdler: does that mean lighting is only made by alpha src?
-			// it sounds ok, but not for polygonsmooth
-			pglBlendFunc(GL_SRC_ALPHA, GL_ZERO);                // 0 alpha = holes in texture
-			break;
-		case PF_Additive & PF_Blending:
-		case PF_Subtractive & PF_Blending:
-		case PF_ReverseSubtract & PF_Blending:
-		case PF_Environment & PF_Blending:
-			pglBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-			break;
-		case PF_AdditiveSource & PF_Blending:
-			pglBlendFunc(GL_SRC_ALPHA, GL_ONE); // src * alpha + dest
-			break;
-		case PF_Multiplicative & PF_Blending:
-			pglBlendFunc(GL_DST_COLOR, GL_ZERO);
-			break;
-		case PF_Fog & PF_Fog:
-			// Sryder: Fog
-			// multiplies input colour by input alpha, and destination colour by input colour, then adds them
-			pglBlendFunc(GL_SRC_ALPHA, GL_SRC_COLOR);
-			break;
-		default: // must be 0, otherwise it's an error
-			// No blending
-			pglBlendFunc(GL_ONE, GL_ZERO);   // the same as no blending
-			break;
-	}
-
-	// Set blending equation
-	switch (flags)
-	{
-		case PF_Subtractive & PF_Blending:
-			SetBlendEquation(GL_FUNC_SUBTRACT);
-			break;
-		case PF_ReverseSubtract & PF_Blending:
-			// good for shadow
-			// not really but what else ?
-			SetBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
-			break;
-		default:
-			SetBlendEquation(GL_FUNC_ADD);
-			break;
-	}
-
-	// Alpha test
-	switch (flags)
-	{
-		case PF_Masked & PF_Blending:
-			pglAlphaFunc(GL_GREATER, 0.5f);
-			break;
-		case PF_Translucent & PF_Blending:
-		case PF_Additive & PF_Blending:
-		case PF_AdditiveSource & PF_Blending:
-		case PF_Subtractive & PF_Blending:
-		case PF_ReverseSubtract & PF_Blending:
-		case PF_Environment & PF_Blending:
-		case PF_Multiplicative & PF_Blending:
-			pglAlphaFunc(GL_NOTEQUAL, 0.0f);
-			break;
-		case PF_Fog & PF_Fog:
-			pglAlphaFunc(GL_ALWAYS, 0.0f); // Don't discard zero alpha fragments
-			break;
-		default:
-			pglAlphaFunc(GL_GREATER, 0.5f);
-			break;
-	}
-}
-static void SetBlend(UINT32 PolyFlags)
-{
-	UINT32 Xor;
-	Xor = CurrentPolyFlags^PolyFlags;
-	if (Xor & (PF_Blending|PF_RemoveYWrap|PF_ForceWrapX|PF_ForceWrapY|PF_Occlude|PF_NoTexture|PF_Modulated|PF_NoDepthTest|PF_Decal|PF_Invisible))
-	{
-		if (Xor & PF_Blending) // if blending mode must be changed
-			SetBlendMode(PolyFlags & PF_Blending);
-
-		if (Xor & PF_NoAlphaTest)
-		{
-			if (PolyFlags & PF_NoAlphaTest)
-				pglDisable(GL_ALPHA_TEST);
-			else
-				pglEnable(GL_ALPHA_TEST);      // discard 0 alpha pixels (holes in texture)
-		}
-
-		if (Xor & PF_Decal)
-		{
-			if (PolyFlags & PF_Decal)
-				pglEnable(GL_POLYGON_OFFSET_FILL);
-			else
-				pglDisable(GL_POLYGON_OFFSET_FILL);
-		}
-
-		if (Xor & PF_NoDepthTest)
-		{
-			if (PolyFlags & PF_NoDepthTest)
-				pglDepthFunc(GL_ALWAYS); //pglDisable(GL_DEPTH_TEST);
-			else
-				pglDepthFunc(GL_LEQUAL); //pglEnable(GL_DEPTH_TEST);
-		}
-
-		if (Xor & PF_RemoveYWrap)
-		{
-			if (PolyFlags & PF_RemoveYWrap)
-				Clamp2D(GL_TEXTURE_WRAP_T);
-		}
-
-		if (Xor & PF_ForceWrapX)
-		{
-			if (PolyFlags & PF_ForceWrapX)
-				pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-		}
-
-		if (Xor & PF_ForceWrapY)
-		{
-			if (PolyFlags & PF_ForceWrapY)
-				pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-		}
-
-		if (Xor & PF_Modulated)
-		{
-			if (PolyFlags & PF_Modulated)
-			{   // mix texture colour with Surface->PolyColor
-				pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-			}
-			else
-			{   // colour from texture is unchanged before blending
-				pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
-			}
-		}
-
-		if (Xor & PF_Occlude) // depth test but (no) depth write
-		{
-			if (PolyFlags&PF_Occlude)
-			{
-				pglDepthMask(1);
-			}
-			else
-				pglDepthMask(0);
-		}
-		////Hurdler: not used if we don't define POLYSKY
-		if (Xor & PF_Invisible)
-		{
-			if (PolyFlags&PF_Invisible)
-				pglBlendFunc(GL_ZERO, GL_ONE);         // transparent blending
-			else
-			{   // big hack: (TODO: manage that better)
-				// we test only for PF_Masked because PF_Invisible is only used
-				// (for now) with it (yeah, that's crappy, sorry)
-				if ((PolyFlags&PF_Blending)==PF_Masked)
-					pglBlendFunc(GL_SRC_ALPHA, GL_ZERO);
-			}
-		}
-		if (PolyFlags & PF_NoTexture)
-		{
-			SetNoTexture();
-		}
-	}
-	CurrentPolyFlags = PolyFlags;
-}
-
 static void UploadTexture(HWRTexture_t *pTexInfo, const GLvoid *pTextureBuffer, GLenum format, boolean update)
 {
 	INT32 w = pTexInfo->width;
@@ -1736,9 +436,6 @@ static void UploadTexture(HWRTexture_t *pTexInfo, const GLvoid *pTextureBuffer,
 	}
 }
 
-// -----------------+
-// UpdateTexture    : Updates the texture data.
-// -----------------+
 static void UpdateTexture(HWRTexture_t *pTexInfo)
 {
 	boolean update = true;
@@ -1867,14 +564,14 @@ static void UpdateTexture(HWRTexture_t *pTexInfo)
 	if (pTexInfo->flags & TF_WRAPX)
 		pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
 	else
-		Clamp2D(GL_TEXTURE_WRAP_S);
+		SetClamp(GL_TEXTURE_WRAP_S);
 
 	if (pTexInfo->flags & TF_WRAPY)
 		pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
 	else
-		Clamp2D(GL_TEXTURE_WRAP_T);
+		SetClamp(GL_TEXTURE_WRAP_T);
 
-	if (GPUMaximumAnisotropy)
+	if (GLExtension_texture_filter_anisotropic && GPUMaximumAnisotropy)
 		pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, AnisotropicFilter);
 }
 
@@ -1902,202 +599,22 @@ static void SetTexture(HWRTexture_t *pTexInfo)
 
 		UpdateTexture(pTexInfo);
 
-		newTex->texture = pTexInfo;
-		newTex->name = (UINT32)pTexInfo->downloaded;
-		newTex->width = (UINT32)pTexInfo->width;
-		newTex->height = (UINT32)pTexInfo->height;
-		newTex->format = (UINT32)pTexInfo->format;
-
-		// insertion at the tail
-		if (TexCacheTail)
-		{
-			newTex->prev = TexCacheTail;
-			TexCacheTail->next = newTex;
-			TexCacheTail = newTex;
-		}
-		else // initialization of the linked list
-			TexCacheTail = TexCacheHead = newTex;
-	}
-}
-
-static void Shader_SetUniforms(FSurfaceInfo *Surface, GLRGBAFloat *poly, GLRGBAFloat *tint, GLRGBAFloat *fade)
-{
-#ifdef GL_SHADERS
-	FShaderObject *shader = ShaderState.current;
-
-	if (ShadersEnabled && (shader != NULL) && pglUseProgram)
-	{
-		if (!shader->program)
-		{
-			pglUseProgram(0);
-			return;
-		}
-
-		if (ShaderState.changed)
-		{
-			pglUseProgram(shader->program);
-			ShaderState.changed = false;
-		}
-
-		// Color uniforms can be left NULL and will be set to white (1.0f, 1.0f, 1.0f, 1.0f)
-		if (poly == NULL)
-			poly = &ShaderDefaultColor;
-		if (tint == NULL)
-			tint = &ShaderDefaultColor;
-		if (fade == NULL)
-			fade = &ShaderDefaultColor;
-
-		#define UNIFORM_1(uniform, a, function) \
-			if (uniform != -1) \
-				function (uniform, a);
-
-		#define UNIFORM_2(uniform, a, b, function) \
-			if (uniform != -1) \
-				function (uniform, a, b);
-
-		#define UNIFORM_3(uniform, a, b, c, function) \
-			if (uniform != -1) \
-				function (uniform, a, b, c);
-
-		#define UNIFORM_4(uniform, a, b, c, d, function) \
-			if (uniform != -1) \
-				function (uniform, a, b, c, d);
-
-		// polygon
-		UNIFORM_4(shader->uniforms[uniform_poly_color], poly->red, poly->green, poly->blue, poly->alpha, pglUniform4f);
-		UNIFORM_4(shader->uniforms[uniform_tint_color], tint->red, tint->green, tint->blue, tint->alpha, pglUniform4f);
-		UNIFORM_4(shader->uniforms[uniform_fade_color], fade->red, fade->green, fade->blue, fade->alpha, pglUniform4f);
-
-		if (Surface != NULL)
-		{
-			UNIFORM_1(shader->uniforms[uniform_lighting], Surface->LightInfo.LightLevel, pglUniform1f);
-			UNIFORM_1(shader->uniforms[uniform_fade_start], Surface->LightInfo.FadeStart, pglUniform1f);
-			UNIFORM_1(shader->uniforms[uniform_fade_end], Surface->LightInfo.FadeEnd, pglUniform1f);
-		}
-
-		UNIFORM_1(shader->uniforms[uniform_leveltime], ((float)ShaderLevelTime) / TICRATE, pglUniform1f);
-
-		#undef UNIFORM_1
-		#undef UNIFORM_2
-		#undef UNIFORM_3
-		#undef UNIFORM_4
-	}
-#else
-	(void)Surface;
-	(void)poly;
-	(void)tint;
-	(void)fade;
-#endif
-}
-
-static boolean Shader_CompileProgram(FShaderObject *shader, GLint i, const GLchar *vert_shader, const GLchar *frag_shader)
-{
-	GLuint gl_vertShader, gl_fragShader;
-	GLint result;
-
-	//
-	// Load and compile vertex shader
-	//
-	gl_vertShader = pglCreateShader(GL_VERTEX_SHADER);
-	if (!gl_vertShader)
-	{
-		GL_MSG_Error("Shader_CompileProgram: Error creating vertex shader %s\n", HWR_GetShaderName(i));
-		return false;
-	}
-
-	pglShaderSource(gl_vertShader, 1, &vert_shader, NULL);
-	pglCompileShader(gl_vertShader);
-
-	// check for compile errors
-	pglGetShaderiv(gl_vertShader, GL_COMPILE_STATUS, &result);
-	if (result == GL_FALSE)
-	{
-		Shader_CompileError("Error compiling vertex shader", gl_vertShader, i);
-		pglDeleteShader(gl_vertShader);
-		return false;
-	}
-
-	//
-	// Load and compile fragment shader
-	//
-	gl_fragShader = pglCreateShader(GL_FRAGMENT_SHADER);
-	if (!gl_fragShader)
-	{
-		GL_MSG_Error("Shader_CompileProgram: Error creating fragment shader %s\n", HWR_GetShaderName(i));
-		pglDeleteShader(gl_vertShader);
-		pglDeleteShader(gl_fragShader);
-		return false;
-	}
-
-	pglShaderSource(gl_fragShader, 1, &frag_shader, NULL);
-	pglCompileShader(gl_fragShader);
-
-	// check for compile errors
-	pglGetShaderiv(gl_fragShader, GL_COMPILE_STATUS, &result);
-	if (result == GL_FALSE)
-	{
-		Shader_CompileError("Error compiling fragment shader", gl_fragShader, i);
-		pglDeleteShader(gl_vertShader);
-		pglDeleteShader(gl_fragShader);
-		return false;
-	}
-
-	shader->program = pglCreateProgram();
-	pglAttachShader(shader->program, gl_vertShader);
-	pglAttachShader(shader->program, gl_fragShader);
-	pglLinkProgram(shader->program);
-
-	// check link status
-	pglGetProgramiv(shader->program, GL_LINK_STATUS, &result);
-
-	// delete the shader objects
-	pglDeleteShader(gl_vertShader);
-	pglDeleteShader(gl_fragShader);
-
-	// couldn't link?
-	if (result != GL_TRUE)
-	{
-		GL_MSG_Error("Shader_CompileProgram: Error linking shader program %s\n", HWR_GetShaderName(i));
-		pglDeleteProgram(shader->program);
-		return false;
-	}
-
-	// 13062019
-#define GETUNI(uniform) pglGetUniformLocation(shader->program, uniform);
-
-	// lighting
-	shader->uniforms[uniform_poly_color] = GETUNI("poly_color");
-	shader->uniforms[uniform_tint_color] = GETUNI("tint_color");
-	shader->uniforms[uniform_fade_color] = GETUNI("fade_color");
-	shader->uniforms[uniform_lighting] = GETUNI("lighting");
-	shader->uniforms[uniform_fade_start] = GETUNI("fade_start");
-	shader->uniforms[uniform_fade_end] = GETUNI("fade_end");
-
-	// misc. (custom shaders)
-	shader->uniforms[uniform_leveltime] = GETUNI("leveltime");
-
-#undef GETUNI
-
-	return true;
-}
-
-static void Shader_CompileError(const char *message, GLuint program, INT32 shadernum)
-{
-	GLchar *infoLog = NULL;
-	GLint logLength;
-
-	pglGetShaderiv(program, GL_INFO_LOG_LENGTH, &logLength);
+		newTex->texture = pTexInfo;
+		newTex->name = (UINT32)pTexInfo->downloaded;
+		newTex->width = (UINT32)pTexInfo->width;
+		newTex->height = (UINT32)pTexInfo->height;
+		newTex->format = (UINT32)pTexInfo->format;
 
-	if (logLength)
-	{
-		infoLog = malloc(logLength);
-		pglGetShaderInfoLog(program, logLength, NULL, infoLog);
+		// insertion at the tail
+		if (TexCacheTail)
+		{
+			newTex->prev = TexCacheTail;
+			TexCacheTail->next = newTex;
+			TexCacheTail = newTex;
+		}
+		else // initialization of the linked list
+			TexCacheTail = TexCacheHead = newTex;
 	}
-
-	GL_MSG_Error("Shader_CompileProgram: %s (%s)\n%s", message, HWR_GetShaderName(shadernum), (infoLog ? infoLog : ""));
-
-	if (infoLog)
-		free(infoLog);
 }
 
 // code that is common between DrawPolygon and DrawIndexedTriangles
@@ -2219,11 +736,12 @@ static void DrawPolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumP
 		pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
 
 	if (PolyFlags & PF_ForceWrapX)
-		Clamp2D(GL_TEXTURE_WRAP_S);
+		SetClamp(GL_TEXTURE_WRAP_S);
 
 	if (PolyFlags & PF_ForceWrapY)
-		Clamp2D(GL_TEXTURE_WRAP_T);
+		SetClamp(GL_TEXTURE_WRAP_T);
 }
+
 static void DrawIndexedTriangles(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags, UINT32 *IndexArray)
 {
 	PreparePolygon(pSurf, pOutVerts, PolyFlags);
@@ -2235,12 +753,6 @@ static void DrawIndexedTriangles(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UIN
 	// the DrawPolygon variant of this has some code about polyflags and wrapping here but havent noticed any problems from omitting it?
 }
 
-static const boolean gl_ext_arb_vertex_buffer_object = true;
-
-#define NULL_VBO_VERTEX ((FSkyVertex*)NULL)
-#define sky_vbo_x (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_VERTEX->x : &sky->data[0].x)
-#define sky_vbo_u (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_VERTEX->u : &sky->data[0].u)
-#define sky_vbo_r (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_VERTEX->r : &sky->data[0].r)
 static void DrawSkyDome(FSkyDome *sky)
 {
 	int i, j;
@@ -2251,13 +763,13 @@ static void DrawSkyDome(FSkyDome *sky)
 	if (sky->rebuild)
 	{
 		// delete VBO when already exists
-		if (gl_ext_arb_vertex_buffer_object)
+		if (GLExtension_vertex_buffer_object)
 		{
 			if (sky->vbo)
 				pglDeleteBuffers(1, &sky->vbo);
 		}
 
-		if (gl_ext_arb_vertex_buffer_object)
+		if (GLExtension_vertex_buffer_object)
 		{
 			// generate a new VBO and get the associated ID
 			pglGenBuffers(1, &sky->vbo);
@@ -2273,7 +785,7 @@ static void DrawSkyDome(FSkyDome *sky)
 	}
 
 	// bind VBO in order to use
-	if (gl_ext_arb_vertex_buffer_object)
+	if (GLExtension_vertex_buffer_object)
 		pglBindBuffer(GL_ARRAY_BUFFER, sky->vbo);
 
 	// activate and specify pointers to arrays
@@ -2318,16 +830,13 @@ static void DrawSkyDome(FSkyDome *sky)
 	pglColor4ubv(white);
 
 	// bind with 0, so, switch back to normal pointer operation
-	if (gl_ext_arb_vertex_buffer_object)
+	if (GLExtension_vertex_buffer_object)
 		pglBindBuffer(GL_ARRAY_BUFFER, 0);
 
 	// deactivate color array
 	pglDisableClientState(GL_COLOR_ARRAY);
 }
 
-// ==========================================================================
-//
-// ==========================================================================
 static void SetState(INT32 State, INT32 Value)
 {
 	switch (State)
@@ -2341,54 +850,21 @@ static void SetState(INT32 State, INT32 Value)
 			break;
 
 		case GPU_STATE_TEXTUREFILTERMODE:
-			switch (Value)
-			{
-				case GPU_TEXFILTER_TRILINEAR:
-					MipmapMinFilter = GL_LINEAR_MIPMAP_LINEAR;
-					MipmapMagFilter = GL_LINEAR;
-					MipmappingEnabled = GL_TRUE;
-					break;
-				case GPU_TEXFILTER_BILINEAR:
-					MipmapMinFilter = MipmapMagFilter = GL_LINEAR;
-					MipmappingEnabled = GL_FALSE;
-					break;
-				case GPU_TEXFILTER_POINTSAMPLED:
-					MipmapMinFilter = MipmapMagFilter = GL_NEAREST;
-					MipmappingEnabled = GL_FALSE;
-					break;
-				case GPU_TEXFILTER_MIXED1:
-					MipmapMinFilter = GL_NEAREST;
-					MipmapMagFilter = GL_LINEAR;
-					MipmappingEnabled = GL_FALSE;
-					break;
-				case GPU_TEXFILTER_MIXED2:
-					MipmapMinFilter = GL_LINEAR;
-					MipmapMagFilter = GL_NEAREST;
-					MipmappingEnabled = GL_FALSE;
-					break;
-				case GPU_TEXFILTER_MIXED3:
-					MipmapMinFilter = GL_LINEAR_MIPMAP_LINEAR;
-					MipmapMagFilter = GL_NEAREST;
-					MipmappingEnabled = GL_TRUE;
-					break;
-				default:
-					MipmapMagFilter = GL_LINEAR;
-					MipmapMinFilter = GL_NEAREST;
-			}
-
+			GLTexture_SetFilterMode(Value);
 			if (!pgluBuild2DMipmaps)
 			{
 				MipmappingEnabled = GL_FALSE;
 				MipmapMinFilter = GL_LINEAR;
 			}
-
-			Flush(); //??? if we want to change filter mode by texture, remove this
+			GLTexture_Flush(); //??? if we want to change filter mode by texture, remove this
 			break;
 
 		case GPU_STATE_TEXTUREANISOTROPICMODE:
-			AnisotropicFilter = min(Value, GPUMaximumAnisotropy);
-			if (GPUMaximumAnisotropy)
-				Flush(); //??? if we want to change filter mode by texture, remove this
+			if (GLExtension_texture_filter_anisotropic && GPUMaximumAnisotropy)
+			{
+				AnisotropicFilter = min(Value, GPUMaximumAnisotropy);
+				GLTexture_Flush(); //??? if we want to change filter mode by texture, remove this
+			}
 			break;
 
 		default:
@@ -2396,201 +872,9 @@ static void SetState(INT32 State, INT32 Value)
 	}
 }
 
-static float *vertBuffer = NULL;
-static float *normBuffer = NULL;
-static size_t lerpBufferSize = 0;
-static short *vertTinyBuffer = NULL;
-static char *normTinyBuffer = NULL;
-static size_t lerpTinyBufferSize = 0;
-
-// Static temporary buffer for doing frame interpolation
-// 'size' is the vertex size
-static void AllocLerpBuffer(size_t size)
-{
-	if (lerpBufferSize >= size)
-		return;
-
-	if (vertBuffer != NULL)
-		free(vertBuffer);
-
-	if (normBuffer != NULL)
-		free(normBuffer);
-
-	lerpBufferSize = size;
-	vertBuffer = malloc(lerpBufferSize);
-	normBuffer = malloc(lerpBufferSize);
-}
-
-// Static temporary buffer for doing frame interpolation
-// 'size' is the vertex size
-static void AllocLerpTinyBuffer(size_t size)
-{
-	if (lerpTinyBufferSize >= size)
-		return;
-
-	if (vertTinyBuffer != NULL)
-		free(vertTinyBuffer);
-
-	if (normTinyBuffer != NULL)
-		free(normTinyBuffer);
-
-	lerpTinyBufferSize = size;
-	vertTinyBuffer = malloc(lerpTinyBufferSize);
-	normTinyBuffer = malloc(lerpTinyBufferSize / 2);
-}
-
-#ifndef GL_STATIC_DRAW
-#define GL_STATIC_DRAW 0x88E4
-#endif
-
-#ifndef GL_ARRAY_BUFFER
-#define GL_ARRAY_BUFFER 0x8892
-#endif
-
-static void CreateModelVBO(mesh_t *mesh, mdlframe_t *frame)
-{
-	int bufferSize = sizeof(vbo64_t)*mesh->numTriangles * 3;
-	vbo64_t *buffer = (vbo64_t*)malloc(bufferSize);
-	vbo64_t *bufPtr = buffer;
-
-	float *vertPtr = frame->vertices;
-	float *normPtr = frame->normals;
-	float *tanPtr = frame->tangents;
-	float *uvPtr = mesh->uvs;
-	float *lightPtr = mesh->lightuvs;
-	char *colorPtr = frame->colors;
-
-	int i;
-	for (i = 0; i < mesh->numTriangles * 3; i++)
-	{
-		bufPtr->x = *vertPtr++;
-		bufPtr->y = *vertPtr++;
-		bufPtr->z = *vertPtr++;
-
-		bufPtr->nx = *normPtr++;
-		bufPtr->ny = *normPtr++;
-		bufPtr->nz = *normPtr++;
-
-		bufPtr->s0 = *uvPtr++;
-		bufPtr->t0 = *uvPtr++;
-
-		if (tanPtr != NULL)
-		{
-			bufPtr->tan0 = *tanPtr++;
-			bufPtr->tan1 = *tanPtr++;
-			bufPtr->tan2 = *tanPtr++;
-		}
-
-		if (lightPtr != NULL)
-		{
-			bufPtr->s1 = *lightPtr++;
-			bufPtr->t1 = *lightPtr++;
-		}
-
-		if (colorPtr)
-		{
-			bufPtr->r = *colorPtr++;
-			bufPtr->g = *colorPtr++;
-			bufPtr->b = *colorPtr++;
-			bufPtr->a = *colorPtr++;
-		}
-		else
-		{
-			bufPtr->r = 255;
-			bufPtr->g = 255;
-			bufPtr->b = 255;
-			bufPtr->a = 255;
-		}
-
-		bufPtr++;
-	}
-
-	pglGenBuffers(1, &frame->vboID);
-	pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID);
-	pglBufferData(GL_ARRAY_BUFFER, bufferSize, buffer, GL_STATIC_DRAW);
-	free(buffer);
-
-	// Don't leave the array buffer bound to the model,
-	// since this is called mid-frame
-	pglBindBuffer(GL_ARRAY_BUFFER, 0);
-}
-
-static void CreateModelVBOTiny(mesh_t *mesh, tinyframe_t *frame)
-{
-	int bufferSize = sizeof(vbotiny_t)*mesh->numTriangles * 3;
-	vbotiny_t *buffer = (vbotiny_t*)malloc(bufferSize);
-	vbotiny_t *bufPtr = buffer;
-
-	short *vertPtr = frame->vertices;
-	char *normPtr = frame->normals;
-	float *uvPtr = mesh->uvs;
-	char *tanPtr = frame->tangents;
-
-	int i;
-	for (i = 0; i < mesh->numVertices; i++)
-	{
-		bufPtr->x = *vertPtr++;
-		bufPtr->y = *vertPtr++;
-		bufPtr->z = *vertPtr++;
-
-		bufPtr->nx = *normPtr++;
-		bufPtr->ny = *normPtr++;
-		bufPtr->nz = *normPtr++;
-
-		bufPtr->s0 = *uvPtr++;
-		bufPtr->t0 = *uvPtr++;
-
-		if (tanPtr)
-		{
-			bufPtr->tanx = *tanPtr++;
-			bufPtr->tany = *tanPtr++;
-			bufPtr->tanz = *tanPtr++;
-		}
-
-		bufPtr++;
-	}
-
-	pglGenBuffers(1, &frame->vboID);
-	pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID);
-	pglBufferData(GL_ARRAY_BUFFER, bufferSize, buffer, GL_STATIC_DRAW);
-	free(buffer);
-
-	// Don't leave the array buffer bound to the model,
-	// since this is called mid-frame
-	pglBindBuffer(GL_ARRAY_BUFFER, 0);
-}
 static void CreateModelVBOs(model_t *model)
 {
-	int i;
-	for (i = 0; i < model->numMeshes; i++)
-	{
-		mesh_t *mesh = &model->meshes[i];
-
-		if (mesh->frames)
-		{
-			int j;
-			for (j = 0; j < model->meshes[i].numFrames; j++)
-			{
-				mdlframe_t *frame = &mesh->frames[j];
-				if (frame->vboID)
-					pglDeleteBuffers(1, &frame->vboID);
-				frame->vboID = 0;
-				CreateModelVBO(mesh, frame);
-			}
-		}
-		else if (mesh->tinyframes)
-		{
-			int j;
-			for (j = 0; j < model->meshes[i].numFrames; j++)
-			{
-				tinyframe_t *frame = &mesh->tinyframes[j];
-				if (frame->vboID)
-					pglDeleteBuffers(1, &frame->vboID);
-				frame->vboID = 0;
-				CreateModelVBOTiny(mesh, frame);
-			}
-		}
-	}
+	GLModel_GenerateVBOs(model);
 }
 
 #define BUFFER_OFFSET(i) ((void*)(i))
@@ -2606,7 +890,7 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32
 
 	boolean useTinyFrames;
 
-	boolean useVBO = true;
+	boolean useVBO = GLExtension_vertex_buffer_object;
 
 	UINT32 flags;
 	int i;
@@ -2807,7 +1091,7 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32
 				int j = 0;
 
 				// Dangit, I soooo want to do this in a GLSL shader...
-				AllocLerpTinyBuffer(mesh->numVertices * sizeof(short) * 3);
+				GLModel_AllocLerpTinyBuffer(mesh->numVertices * sizeof(short) * 3);
 				vertPtr = vertTinyBuffer;
 				normPtr = normTinyBuffer;
 
@@ -2861,7 +1145,7 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32
 				int j = 0;
 
 				// Dangit, I soooo want to do this in a GLSL shader...
-				AllocLerpBuffer(mesh->numVertices * sizeof(float) * 3);
+				GLModel_AllocLerpBuffer(mesh->numVertices * sizeof(float) * 3);
 				vertPtr = vertBuffer;
 				normPtr = normBuffer;
 				//int j = 0;
@@ -2898,7 +1182,7 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32
 }
 
 // -----------------+
-// HWRAPI DrawModel : Draw a model
+// DrawModel        : Renders a model
 // -----------------+
 static void DrawModel(model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface)
 {
@@ -2973,30 +1257,13 @@ static void SetTransform(FTransform *stransform)
 	pglGetFloatv(GL_MODELVIEW_MATRIX, ModelMatrix); // added for new coronas' code (without depth buffer)
 
 }
+
 static INT32 GetTextureUsed(void)
 {
-	FTextureInfo *tmp = TexCacheHead;
-	INT32 res = 0;
-
-	while (tmp)
-	{
-		// Figure out the correct bytes-per-pixel for this texture
-		// This follows format2bpp in hw_cache.c
-		INT32 bpp = 1;
-		UINT32 format = tmp->format;
-		if (format == GPU_TEXFMT_RGBA)
-			bpp = 4;
-		else if (format == GPU_TEXFMT_ALPHA_INTENSITY_88 || format == GPU_TEXFMT_AP_88)
-			bpp = 2;
-
-		// Add it up!
-		res += tmp->height*tmp->width*bpp;
-		tmp = tmp->next;
-	}
-
-	return res;
+	return GLTexture_GetMemoryUsage(TexCacheHead);
 }
-static void PostImgRedraw(float points[SCREENVERTS][SCREENVERTS][2])
+
+static void PostImgRedraw(float points[GPU_POSTIMGVERTS][GPU_POSTIMGVERTS][2])
 {
 	INT32 x, y;
 	float float_x, float_y, float_nextx, float_nexty;
@@ -3012,14 +1279,14 @@ static void PostImgRedraw(float points[SCREENVERTS][SCREENVERTS][2])
 	};
 
 	// Use a power of two texture, dammit
-	if(GPUScreenWidth <= 1024)
+	if (GPUScreenWidth <= 1024)
 		texsize = 1024;
-	if(GPUScreenWidth <= 512)
+	if (GPUScreenWidth <= 512)
 		texsize = 512;
 
 	// X/Y stretch fix for all resolutions(!)
-	xfix = (float)(texsize)/((float)((GPUScreenWidth)/(float)(SCREENVERTS-1)));
-	yfix = (float)(texsize)/((float)((GPUScreenHeight)/(float)(SCREENVERTS-1)));
+	xfix = (float)(texsize)/((float)((GPUScreenWidth)/(float)(GPU_POSTIMGVERTS-1)));
+	yfix = (float)(texsize)/((float)((GPUScreenHeight)/(float)(GPU_POSTIMGVERTS-1)));
 
 	pglDisable(GL_DEPTH_TEST);
 	pglDisable(GL_BLEND);
@@ -3034,9 +1301,9 @@ static void PostImgRedraw(float points[SCREENVERTS][SCREENVERTS][2])
 	pglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 
 	pglEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	for(x=0;x<SCREENVERTS-1;x++)
+	for(x=0;x<GPU_POSTIMGVERTS-1;x++)
 	{
-		for(y=0;y<SCREENVERTS-1;y++)
+		for(y=0;y<GPU_POSTIMGVERTS-1;y++)
 		{
 			float stCoords[8];
 			float vertCoords[12];
@@ -3098,46 +1365,16 @@ static void FlushScreenTextures(void)
 	WipeStartTexture = WipeEndTexture = 0;
 }
 
-static void GenerateScreenTexture(GLuint *name)
-{
-	INT32 texsize = 2048;
-	boolean firstTime = ((*name) == 0);
-
-	// Use a power of two texture, dammit
-	if(GPUScreenWidth <= 512)
-		texsize = 512;
-	else if (GPUScreenWidth <= 1024)
-		texsize = 1024;
-
-	// Create screen texture
-	if (firstTime)
-		pglGenTextures(1, name);
-	pglBindTexture(GL_TEXTURE_2D, *name);
-
-	if (firstTime)
-	{
-		pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-		pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-		Clamp2D(GL_TEXTURE_WRAP_S);
-		Clamp2D(GL_TEXTURE_WRAP_T);
-		pglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, texsize, texsize, 0);
-	}
-	else
-		pglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, texsize, texsize);
-
-	CurrentTexture = *name;
-}
-
 // Create screen to fade from
 static void StartScreenWipe(void)
 {
-	GenerateScreenTexture(&WipeStartTexture);
+	GLTexture_GenerateScreenTexture(&WipeStartTexture);
 }
 
 // Create screen to fade to
 static void EndScreenWipe(void)
 {
-	GenerateScreenTexture(&WipeEndTexture);
+	GLTexture_GenerateScreenTexture(&WipeEndTexture);
 }
 
 // Draw the last scene under the intermission
@@ -3146,7 +1383,7 @@ static void DrawIntermissionBG(void)
 	float xfix, yfix;
 	INT32 texsize = 2048;
 
-	const float screenVerts[12] =
+	const float GPU_POSTIMGVERTS[12] =
 	{
 		-1.0f, -1.0f, 1.0f,
 		-1.0f, 1.0f, 1.0f,
@@ -3156,15 +1393,15 @@ static void DrawIntermissionBG(void)
 
 	float fix[8];
 
-	if(GPUScreenWidth <= 1024)
+	if (GPUScreenWidth <= 1024)
 		texsize = 1024;
-	if(GPUScreenWidth <= 512)
+	if (GPUScreenWidth <= 512)
 		texsize = 512;
 
 	xfix = 1/((float)(texsize)/((float)((GPUScreenWidth))));
 	yfix = 1/((float)(texsize)/((float)((GPUScreenHeight))));
 
-	// const float screenVerts[12]
+	// const float GPU_POSTIMGVERTS[12]
 
 	// float fix[8];
 	fix[0] = 0.0f;
@@ -3182,7 +1419,7 @@ static void DrawIntermissionBG(void)
 	pglColor4ubv(white);
 
 	pglTexCoordPointer(2, GL_FLOAT, 0, fix);
-	pglVertexPointer(3, GL_FLOAT, 0, screenVerts);
+	pglVertexPointer(3, GL_FLOAT, 0, GPU_POSTIMGVERTS);
 	pglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 
 	CurrentTexture = ScreenTexture;
@@ -3196,7 +1433,7 @@ static void DoScreenWipe(void)
 
 	INT32 fademaskdownloaded = CurrentTexture; // the fade mask that has been set
 
-	const float screenVerts[12] =
+	const float GPU_POSTIMGVERTS[12] =
 	{
 		-1.0f, -1.0f, 1.0f,
 		-1.0f, 1.0f, 1.0f,
@@ -3214,16 +1451,19 @@ static void DoScreenWipe(void)
 		1.0f, 1.0f
 	};
 
+	if (!GLExtension_multitexture)
+		return;
+
 	// Use a power of two texture, dammit
-	if(GPUScreenWidth <= 1024)
+	if (GPUScreenWidth <= 1024)
 		texsize = 1024;
-	if(GPUScreenWidth <= 512)
+	if (GPUScreenWidth <= 512)
 		texsize = 512;
 
 	xfix = 1/((float)(texsize)/((float)((GPUScreenWidth))));
 	yfix = 1/((float)(texsize)/((float)((GPUScreenHeight))));
 
-	// const float screenVerts[12]
+	// const float GPU_POSTIMGVERTS[12]
 
 	// float fix[8];
 	fix[0] = 0.0f;
@@ -3244,7 +1484,7 @@ static void DoScreenWipe(void)
 	pglBindTexture(GL_TEXTURE_2D, WipeStartTexture);
 	pglColor4ubv(white);
 	pglTexCoordPointer(2, GL_FLOAT, 0, fix);
-	pglVertexPointer(3, GL_FLOAT, 0, screenVerts);
+	pglVertexPointer(3, GL_FLOAT, 0, GPU_POSTIMGVERTS);
 	pglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 
 	SetBlend(PF_Modulated|PF_Translucent|PF_NoDepthTest);
@@ -3265,7 +1505,7 @@ static void DoScreenWipe(void)
 
 	pglClientActiveTexture(GL_TEXTURE0);
 	pglTexCoordPointer(2, GL_FLOAT, 0, fix);
-	pglVertexPointer(3, GL_FLOAT, 0, screenVerts);
+	pglVertexPointer(3, GL_FLOAT, 0, GPU_POSTIMGVERTS);
 	pglClientActiveTexture(GL_TEXTURE1);
 	pglEnableClientState(GL_TEXTURE_COORD_ARRAY);
 	pglTexCoordPointer(2, GL_FLOAT, 0, defaultST);
@@ -3282,12 +1522,12 @@ static void DoScreenWipe(void)
 // Create a texture from the screen.
 static void MakeScreenTexture(void)
 {
-	GenerateScreenTexture(&ScreenTexture);
+	GLTexture_GenerateScreenTexture(&ScreenTexture);
 }
 
 static void MakeScreenFinalTexture(void)
 {
-	GenerateScreenTexture(&FinalScreenTexture);
+	GLTexture_GenerateScreenTexture(&FinalScreenTexture);
 }
 
 static void DrawScreenFinalTexture(int width, int height)
@@ -3301,9 +1541,9 @@ static void DrawScreenFinalTexture(int width, int height)
 	float off[12];
 	float fix[8];
 
-	if(GPUScreenWidth <= 1024)
+	if (GPUScreenWidth <= 1024)
 		texsize = 1024;
-	if(GPUScreenWidth <= 512)
+	if (GPUScreenWidth <= 512)
 		texsize = 512;
 
 	xfix = 1/((float)(texsize)/((float)((GPUScreenWidth))));
@@ -3362,6 +1602,60 @@ static void DrawScreenFinalTexture(int width, int height)
 	CurrentTexture = FinalScreenTexture;
 }
 
+static void SetShader(int type)
+{
+#ifdef GL_SHADERS
+	Shader_Set(GLBackend_GetShaderType(type));
+#else
+	(void)type;
+#endif
+}
+
+static boolean CompileShaders(void)
+{
+#ifdef GL_SHADERS
+	return Shader_Compile();
+#else
+	return false;
+#endif
+}
+
+static void SetShaderInfo(INT32 info, INT32 value)
+{
+#ifdef GL_SHADERS
+	Shader_SetInfo(info, value);
+#else
+	(void)info;
+	(void)value;
+#endif
+}
+
+static void LoadCustomShader(int number, char *shader, size_t size, boolean fragment)
+{
+#ifdef GL_SHADERS
+	Shader_LoadCustom(number, shader, size, fragment);
+#else
+	(void)number;
+	(void)shader;
+	(void)size;
+	(void)fragment;
+#endif
+}
+
+static void UnSetShader(void)
+{
+#ifdef GL_SHADERS
+	Shader_UnSet();
+#endif
+}
+
+static void CleanShaders(void)
+{
+#ifdef GL_SHADERS
+	Shader_Clean();
+#endif
+}
+
 struct GPURenderingAPI GLInterfaceAPI = {
 	Init,
 	NULL,
@@ -3411,4 +1705,82 @@ struct GPURenderingAPI GLInterfaceAPI = {
 	LoadCustomShader,
 };
 
+// -----------------+
+// SetModelView     :
+// -----------------+
+void SetModelView(GLint w, GLint h)
+{
+//	GL_DBG_Printf("SetModelView(): %dx%d\n", (int)w, (int)h);
+
+	// The screen textures need to be flushed if the width or height change so that they be remade for the correct size
+	if (GPUScreenWidth != w || GPUScreenHeight != h)
+		FlushScreenTextures();
+
+	GPUScreenWidth = w;
+	GPUScreenHeight = h;
+
+	pglViewport(0, 0, w, h);
+
+	pglMatrixMode(GL_PROJECTION);
+	pglLoadIdentity();
+
+	pglMatrixMode(GL_MODELVIEW);
+	pglLoadIdentity();
+
+	GLPerspective(FIELD_OF_VIEW, ASPECT_RATIO);
+
+	// added for new coronas' code (without depth buffer)
+	pglGetIntegerv(GL_VIEWPORT, SceneViewport);
+	pglGetFloatv(GL_PROJECTION_MATRIX, ProjectionMatrix);
+}
+
+
+// -----------------+
+// SetStates        : Set permanent states
+// -----------------+
+void SetStates(void)
+{
+#ifdef GL_LIGHT_MODEL_AMBIENT
+	GLfloat LightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
+#endif
+
+	pglEnable(GL_TEXTURE_2D);      // two-dimensional texturing
+	pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+	pglEnable(GL_BLEND);           // enable color blending
+	pglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+
+	pglEnable(GL_ALPHA_TEST);
+	pglAlphaFunc(GL_NOTEQUAL, 0.0f);
+
+	pglEnable(GL_DEPTH_TEST);    // check the depth buffer
+	pglDepthMask(GL_TRUE);             // enable writing to depth buffer
+
+	pglClearDepth(1.0f);
+	pglDepthRange(0.0f, 1.0f);
+	pglDepthFunc(GL_LEQUAL);
+
+	// Hurdler: not necessary, is it?
+	pglShadeModel(GL_SMOOTH);      // iterate vertice colors
+
+	// this set CurrentPolyFlags to the actual configuration
+	CurrentPolyFlags = 0xFFFFFFFF;
+	SetBlend(0);
+
+	CurrentTexture = 0;
+	SetNoTexture();
+
+	pglPolygonOffset(-1.0f, -1.0f);
+
+	pglLoadIdentity();
+	pglScalef(1.0f, 1.0f, -1.0f);
+	pglGetFloatv(GL_MODELVIEW_MATRIX, ModelMatrix); // added for new coronas' code (without depth buffer)
+
+	// Lighting for models
+#ifdef GL_LIGHT_MODEL_AMBIENT
+	pglLightModelfv(GL_LIGHT_MODEL_AMBIENT, LightDiffuse);
+	pglEnable(GL_LIGHT0);
+#endif
+}
+
 #endif //HWRENDER
diff --git a/src/hardware/r_opengl/r_opengl.h b/src/hardware/r_opengl/r_opengl.h
index 5ce8670afe58abe086b1f71ca28053ef9825e461..6c864373019dba29ab20e0bb29d04e2445edb0a8 100644
--- a/src/hardware/r_opengl/r_opengl.h
+++ b/src/hardware/r_opengl/r_opengl.h
@@ -1,21 +1,14 @@
-// Emacs style mode select   -*- C++ -*-
+// SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-//
 // Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1998-2020 by Sonic Team Junior.
 //
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// 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 General Public License for more details.
-//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
 //-----------------------------------------------------------------------------
-/// \file
-/// \brief OpenGL API for Doom Legacy
+/// \file r_opengl.h
+/// \brief OpenGL API for Sonic Robo Blast 2
 
 #ifndef _R_OPENGL_H_
 #define _R_OPENGL_H_
@@ -27,7 +20,9 @@
 #pragma warning(disable : 4214 4244)
 #endif
 
+#ifndef HAVE_GLES2
 #include "SDL_opengl.h" //Alam_GBC: Simple, yes?
+#endif
 
 #ifdef _MSC_VER
 #pragma warning(default : 4214 4244)
@@ -66,27 +61,12 @@
 //#undef DEBUG_TO_FILE
 //#endif
 
-#ifdef DEBUG_TO_FILE
-extern FILE             *gllogstream;
-#endif
+#include "../r_glcommon/r_glcommon.h"
 
 // ==========================================================================
 //                                                                     PROTOS
 // ==========================================================================
 
-void *GetGLFunc(const char *proc);
-boolean SetupGLfunc(void);
-void SetupGLFunc4(void);
-void Flush(void);
-INT32 isExtAvailable(const char *extension, const GLubyte *start);
-void SetModelView(GLint w, GLint h);
-void SetStates(void);
-
-#ifndef GL_EXT_texture_filter_anisotropic
-#define GL_TEXTURE_MAX_ANISOTROPY_EXT     0x84FE
-#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
-#endif
-
 #ifdef USE_WGL_SWAP
 typedef BOOL (APIENTRY *PFNWGLEXTSWAPCONTROLPROC) (int);
 typedef int (APIENTRY *PFNWGLEXTGETSWAPINTERVALPROC) (void);
@@ -94,39 +74,4 @@ extern PFNWGLEXTSWAPCONTROLPROC wglSwapIntervalEXT;
 extern PFNWGLEXTGETSWAPINTERVALPROC wglGetSwapIntervalEXT;
 #endif
 
-#ifdef STATIC_OPENGL
-#define pglClear glClear
-#define pglGetIntegerv glGetIntegerv
-#define pglGetString glGetString
-#else
-/* 1.0 Miscellaneous functions */
-typedef void (APIENTRY * PFNglClear) (GLbitfield mask);
-extern PFNglClear pglClear;
-typedef void (APIENTRY * PFNglGetIntegerv) (GLenum pname, GLint *params);
-extern PFNglGetIntegerv pglGetIntegerv;
-typedef const GLubyte* (APIENTRY  * PFNglGetString) (GLenum name);
-extern PFNglGetString pglGetString;
-#if 0
-typedef void (APIENTRY * PFNglEnableClientState) (GLenum cap); // redefined in r_opengl.c
-static PFNglEnableClientState pglEnableClientState;
-#endif
-#endif
-
-// ==========================================================================
-//                                                                     GLOBAL
-// ==========================================================================
-
-extern const GLubyte   *GLVersion;
-extern const GLubyte   *GLRenderer;
-extern const GLubyte   *GLExtensions;
-
-extern GLint            GPUTextureFormat;
-extern RGBA_t           GPUTexturePalette[256];
-extern GLint            GPUScreenWidth;
-extern GLint            GPUScreenHeight;
-extern GLbyte           GPUScreenDepth;
-extern GLint            GPUMaximumAnisotropy;
-
-extern struct GPURenderingAPI GLInterfaceAPI;
-
 #endif
diff --git a/src/hardware/shaders/gl_shaders.c b/src/hardware/shaders/gl_shaders.c
new file mode 100644
index 0000000000000000000000000000000000000000..756300cd7b41e3e7d66791720ec2fcb98a243c32
--- /dev/null
+++ b/src/hardware/shaders/gl_shaders.c
@@ -0,0 +1,613 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2020 by Jaime "Lactozilla" Passos.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file gl_shaders.c
+/// \brief OpenGL shaders
+
+#include "gl_shaders.h"
+#include "../r_glcommon/r_glcommon.h"
+
+GLboolean ShadersEnabled = GL_FALSE;
+INT32 ShadersAllowed = GPU_SHADEROPTION_OFF;
+
+typedef GLuint (R_GL_APIENTRY *PFNglCreateShader)       (GLenum);
+typedef void   (R_GL_APIENTRY *PFNglShaderSource)       (GLuint, GLsizei, const GLchar**, GLint*);
+typedef void   (R_GL_APIENTRY *PFNglCompileShader)      (GLuint);
+typedef void   (R_GL_APIENTRY *PFNglGetShaderiv)        (GLuint, GLenum, GLint*);
+typedef void   (R_GL_APIENTRY *PFNglGetShaderInfoLog)   (GLuint, GLsizei, GLsizei*, GLchar*);
+typedef void   (R_GL_APIENTRY *PFNglDeleteShader)       (GLuint);
+typedef GLuint (R_GL_APIENTRY *PFNglCreateProgram)      (void);
+typedef void   (R_GL_APIENTRY *PFNglDeleteProgram)      (GLuint);
+typedef void   (R_GL_APIENTRY *PFNglAttachShader)       (GLuint, GLuint);
+typedef void   (R_GL_APIENTRY *PFNglLinkProgram)        (GLuint);
+typedef void   (R_GL_APIENTRY *PFNglGetProgramiv)       (GLuint, GLenum, GLint*);
+typedef void   (R_GL_APIENTRY *PFNglUseProgram)         (GLuint);
+typedef void   (R_GL_APIENTRY *PFNglUniform1i)          (GLint, GLint);
+typedef void   (R_GL_APIENTRY *PFNglUniform1f)          (GLint, GLfloat);
+typedef void   (R_GL_APIENTRY *PFNglUniform2f)          (GLint, GLfloat, GLfloat);
+typedef void   (R_GL_APIENTRY *PFNglUniform3f)          (GLint, GLfloat, GLfloat, GLfloat);
+typedef void   (R_GL_APIENTRY *PFNglUniform4f)          (GLint, GLfloat, GLfloat, GLfloat, GLfloat);
+typedef void   (R_GL_APIENTRY *PFNglUniform1fv)         (GLint, GLsizei, const GLfloat*);
+typedef void   (R_GL_APIENTRY *PFNglUniform2fv)         (GLint, GLsizei, const GLfloat*);
+typedef void   (R_GL_APIENTRY *PFNglUniform3fv)         (GLint, GLsizei, const GLfloat*);
+typedef void   (R_GL_APIENTRY *PFNglUniformMatrix4fv)   (GLint, GLsizei, GLboolean, const GLfloat *);
+typedef GLint  (R_GL_APIENTRY *PFNglGetUniformLocation) (GLuint, const GLchar*);
+typedef GLint  (R_GL_APIENTRY *PFNglGetAttribLocation)  (GLuint, const GLchar*);
+
+static PFNglCreateShader pglCreateShader;
+static PFNglShaderSource pglShaderSource;
+static PFNglCompileShader pglCompileShader;
+static PFNglGetShaderiv pglGetShaderiv;
+static PFNglGetShaderInfoLog pglGetShaderInfoLog;
+static PFNglDeleteShader pglDeleteShader;
+static PFNglCreateProgram pglCreateProgram;
+static PFNglDeleteProgram pglDeleteProgram;
+static PFNglAttachShader pglAttachShader;
+static PFNglLinkProgram pglLinkProgram;
+static PFNglGetProgramiv pglGetProgramiv;
+static PFNglUseProgram pglUseProgram;
+static PFNglUniform1i pglUniform1i;
+static PFNglUniform1f pglUniform1f;
+static PFNglUniform2f pglUniform2f;
+static PFNglUniform3f pglUniform3f;
+static PFNglUniform4f pglUniform4f;
+static PFNglUniform1fv pglUniform1fv;
+static PFNglUniform2fv pglUniform2fv;
+static PFNglUniform3fv pglUniform3fv;
+static PFNglUniformMatrix4fv pglUniformMatrix4fv;
+static PFNglGetUniformLocation pglGetUniformLocation;
+static PFNglGetAttribLocation pglGetAttribLocation;
+
+FShaderObject ShaderObjects[HWR_MAXSHADERS];
+FShaderObject UserShaderObjects[HWR_MAXSHADERS];
+FShaderSource CustomShaders[HWR_MAXSHADERS];
+
+FShaderState ShaderState;
+
+static GLRGBAFloat ShaderDefaultColor = {1.0f, 1.0f, 1.0f, 1.0f};
+
+// Shader info
+static INT32 ShaderLevelTime = 0;
+
+#ifdef HAVE_GLES2
+#include "shaders_gles2.h"
+#else
+#include "shaders_gl2.h"
+#endif
+
+// ================
+//  Shader sources
+// ================
+
+static struct {
+	const char *vertex;
+	const char *fragment;
+} const ShaderSources[] = {
+	// Default shader
+	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_DEFAULT_FRAGMENT_SHADER},
+
+	// Floor shader
+	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER},
+
+	// Wall shader
+	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER},
+
+	// Sprite shader
+	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER},
+
+	// Model shader
+	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER},
+
+	// Model shader + diffuse lighting from above
+	{GLSL_MODEL_LIGHTING_VERTEX_SHADER, GLSL_SOFTWARE_MODEL_LIGHTING_FRAGMENT_SHADER},
+
+	// Water shader
+	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_WATER_FRAGMENT_SHADER},
+
+	// Fog shader
+	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_FOG_FRAGMENT_SHADER},
+
+	// Sky shader
+	{GLSL_DEFAULT_VERTEX_SHADER, GLSL_SKY_FRAGMENT_SHADER},
+
+	{NULL, NULL},
+};
+
+void Shader_LoadFunctions(void)
+{
+	pglCreateShader = GLBackend_GetFunction("glCreateShader");
+	pglShaderSource = GLBackend_GetFunction("glShaderSource");
+	pglCompileShader = GLBackend_GetFunction("glCompileShader");
+	pglGetShaderiv = GLBackend_GetFunction("glGetShaderiv");
+	pglGetShaderInfoLog = GLBackend_GetFunction("glGetShaderInfoLog");
+	pglDeleteShader = GLBackend_GetFunction("glDeleteShader");
+	pglCreateProgram = GLBackend_GetFunction("glCreateProgram");
+	pglDeleteProgram = GLBackend_GetFunction("glDeleteProgram");
+	pglAttachShader = GLBackend_GetFunction("glAttachShader");
+	pglLinkProgram = GLBackend_GetFunction("glLinkProgram");
+	pglGetProgramiv = GLBackend_GetFunction("glGetProgramiv");
+	pglUseProgram = GLBackend_GetFunction("glUseProgram");
+	pglUniform1i = GLBackend_GetFunction("glUniform1i");
+	pglUniform1f = GLBackend_GetFunction("glUniform1f");
+	pglUniform2f = GLBackend_GetFunction("glUniform2f");
+	pglUniform3f = GLBackend_GetFunction("glUniform3f");
+	pglUniform4f = GLBackend_GetFunction("glUniform4f");
+	pglUniform1fv = GLBackend_GetFunction("glUniform1fv");
+	pglUniform2fv = GLBackend_GetFunction("glUniform2fv");
+	pglUniform3fv = GLBackend_GetFunction("glUniform3fv");
+	pglUniformMatrix4fv = GLBackend_GetFunction("glUniformMatrix4fv");
+	pglGetUniformLocation = GLBackend_GetFunction("glGetUniformLocation");
+	pglGetAttribLocation = GLBackend_GetFunction("glGetAttribLocation");
+}
+
+#ifdef HAVE_GLES2
+int Shader_AttribLoc(int loc)
+{
+	FShaderObject *shader = ShaderState.current;
+	int pos, attrib;
+
+	EShaderAttribute LOC_TO_ATTRIB[attrib_max] =
+	{
+		attrib_position,     // LOC_POSITION
+		attrib_texcoord,     // LOC_TEXCOORD + LOC_TEXCOORD0
+		attrib_normal,       // LOC_NORMAL
+		attrib_colors,       // LOC_COLORS
+		attrib_fadetexcoord, // LOC_TEXCOORD1
+	};
+
+	if (shader == NULL)
+		I_Error("Shader_AttribLoc: shader not set");
+
+	attrib = LOC_TO_ATTRIB[loc];
+	pos = shader->attributes[attrib];
+
+	if (pos == -1)
+		return 0;
+
+	return pos;
+}
+#endif
+
+//
+// Shader info
+// Those are given to the uniforms.
+//
+
+void Shader_SetInfo(INT32 info, INT32 value)
+{
+	switch (info)
+	{
+		case GPU_SHADERINFO_LEVELTIME:
+			ShaderLevelTime = value;
+			break;
+		default:
+			break;
+	}
+}
+
+//
+// Custom shader loading
+//
+void Shader_LoadCustom(int number, char *code, size_t size, boolean isfragment)
+{
+	FShaderSource *shader;
+
+	if (!GLExtension_shaders)
+		return;
+
+	if (number < 1 || number > HWR_MAXSHADERS)
+		I_Error("Shader_LoadCustom: cannot load shader %d (min 1, max %d)", number, HWR_MAXSHADERS);
+	else if (code == NULL)
+		I_Error("Shader_LoadCustom: empty shader");
+
+	shader = &CustomShaders[number];
+
+#define COPYSHADER(source) { \
+	if (shader->source) \
+		free(shader->source); \
+	shader->source = malloc(size+1); \
+	strncpy(shader->source, code, size); \
+	shader->source[size] = 0; \
+	}
+
+	if (isfragment)
+		COPYSHADER(fragment)
+	else
+		COPYSHADER(vertex)
+}
+
+void Shader_Set(int type)
+{
+	FShaderObject *shader = ShaderState.current;
+
+#ifndef HAVE_GLES2
+	if (ShadersAllowed == GPU_SHADEROPTION_OFF)
+		return;
+#endif
+
+	if ((shader == NULL) || (GLuint)type != ShaderState.type)
+	{
+		FShaderObject *baseshader = &ShaderObjects[type];
+		FShaderObject *usershader = &UserShaderObjects[type];
+
+		if (usershader->program)
+			shader = (ShadersAllowed == GPU_SHADEROPTION_NOCUSTOM) ? baseshader : usershader;
+		else
+			shader = baseshader;
+
+		ShaderState.current = shader;
+		ShaderState.type = type;
+		ShaderState.changed = true;
+	}
+
+	if (ShaderState.program != shader->program)
+	{
+		ShaderState.program = shader->program;
+		ShaderState.changed = true;
+	}
+
+#ifdef HAVE_GLES2
+	Shader_SetTransform();
+	ShadersEnabled = true;
+#else
+	ShadersEnabled = (shader->program != 0);
+#endif
+}
+
+void Shader_UnSet(void)
+{
+#ifdef HAVE_GLES2
+	Shader_Set(SHADER_DEFAULT);
+	Shader_SetUniforms(NULL, NULL, NULL, NULL);
+#else
+	ShaderState.current = NULL;
+	ShaderState.type = 0;
+	ShaderState.program = 0;
+
+	if (pglUseProgram)
+		pglUseProgram(0);
+	ShadersEnabled = false;
+#endif
+}
+
+void Shader_Clean(void)
+{
+	INT32 i;
+
+	for (i = 1; i < HWR_MAXSHADERS; i++)
+	{
+		FShaderSource *shader = &CustomShaders[i];
+
+		if (shader->vertex)
+			free(shader->vertex);
+
+		if (shader->fragment)
+			free(shader->fragment);
+
+		shader->vertex = NULL;
+		shader->fragment = NULL;
+	}
+}
+
+#ifdef HAVE_GLES2
+#define Shader_ErrorMessage I_Error
+#else
+#define Shader_ErrorMessage GL_MSG_Error
+#endif
+
+static void Shader_CompileError(const char *message, GLuint program, INT32 shadernum)
+{
+	GLchar *infoLog = NULL;
+	GLint logLength;
+
+	pglGetShaderiv(program, GL_INFO_LOG_LENGTH, &logLength);
+
+	if (logLength)
+	{
+		infoLog = malloc(logLength);
+		pglGetShaderInfoLog(program, logLength, NULL, infoLog);
+	}
+
+	Shader_ErrorMessage("Shader_CompileProgram: %s (%s)\n%s", message, HWR_GetShaderName(shadernum), (infoLog ? infoLog : ""));
+
+	if (infoLog)
+		free(infoLog);
+}
+
+static boolean Shader_CompileProgram(FShaderObject *shader, GLint i, const GLchar *vert_shader, const GLchar *frag_shader)
+{
+	GLuint gl_vertShader, gl_fragShader;
+	GLint result;
+
+	//
+	// Load and compile vertex shader
+	//
+	gl_vertShader = pglCreateShader(GL_VERTEX_SHADER);
+	if (!gl_vertShader)
+	{
+		Shader_ErrorMessage("Shader_CompileProgram: Error creating vertex shader %s\n", HWR_GetShaderName(i));
+		return false;
+	}
+
+	pglShaderSource(gl_vertShader, 1, &vert_shader, NULL);
+	pglCompileShader(gl_vertShader);
+
+	// check for compile errors
+	pglGetShaderiv(gl_vertShader, GL_COMPILE_STATUS, &result);
+	if (result == GL_FALSE)
+	{
+		Shader_CompileError("Error compiling vertex shader", gl_vertShader, i);
+		pglDeleteShader(gl_vertShader);
+		return false;
+	}
+
+	//
+	// Load and compile fragment shader
+	//
+	gl_fragShader = pglCreateShader(GL_FRAGMENT_SHADER);
+	if (!gl_fragShader)
+	{
+		Shader_ErrorMessage("Shader_CompileProgram: Error creating fragment shader %s\n", HWR_GetShaderName(i));
+		pglDeleteShader(gl_vertShader);
+		pglDeleteShader(gl_fragShader);
+		return false;
+	}
+
+	pglShaderSource(gl_fragShader, 1, &frag_shader, NULL);
+	pglCompileShader(gl_fragShader);
+
+	// check for compile errors
+	pglGetShaderiv(gl_fragShader, GL_COMPILE_STATUS, &result);
+	if (result == GL_FALSE)
+	{
+		Shader_CompileError("Error compiling fragment shader", gl_fragShader, i);
+		pglDeleteShader(gl_vertShader);
+		pglDeleteShader(gl_fragShader);
+		return false;
+	}
+
+	shader->program = pglCreateProgram();
+	pglAttachShader(shader->program, gl_vertShader);
+	pglAttachShader(shader->program, gl_fragShader);
+	pglLinkProgram(shader->program);
+
+	// check link status
+	pglGetProgramiv(shader->program, GL_LINK_STATUS, &result);
+
+	// delete the shader objects
+	pglDeleteShader(gl_vertShader);
+	pglDeleteShader(gl_fragShader);
+
+	// couldn't link?
+	if (result != GL_TRUE)
+	{
+		Shader_ErrorMessage("Shader_CompileProgram: Error linking shader program %s\n", HWR_GetShaderName(i));
+		pglDeleteProgram(shader->program);
+		return false;
+	}
+
+#define GETUNI(uniform) pglGetUniformLocation(shader->program, uniform)
+
+#ifdef HAVE_GLES2
+	memset(shader->projMatrix, 0x00, sizeof(fmatrix4_t));
+	memset(shader->viewMatrix, 0x00, sizeof(fmatrix4_t));
+	memset(shader->modelMatrix, 0x00, sizeof(fmatrix4_t));
+
+	// transform
+	shader->uniforms[uniform_model]       = GETUNI("u_model");
+	shader->uniforms[uniform_view]        = GETUNI("u_view");
+	shader->uniforms[uniform_projection]  = GETUNI("u_projection");
+
+	// samplers
+	shader->uniforms[uniform_startscreen] = GETUNI("t_startscreen");
+	shader->uniforms[uniform_endscreen]   = GETUNI("t_endscreen");
+	shader->uniforms[uniform_fademask]    = GETUNI("t_fademask");
+
+	// misc.
+	shader->uniforms[uniform_isfadingin]  = GETUNI("is_fading_in");
+	shader->uniforms[uniform_istowhite]   = GETUNI("is_to_white");
+#endif
+
+	// lighting
+	shader->uniforms[uniform_poly_color]  = GETUNI("poly_color");
+	shader->uniforms[uniform_tint_color]  = GETUNI("tint_color");
+	shader->uniforms[uniform_fade_color]  = GETUNI("fade_color");
+	shader->uniforms[uniform_lighting]    = GETUNI("lighting");
+	shader->uniforms[uniform_fade_start]  = GETUNI("fade_start");
+	shader->uniforms[uniform_fade_end]    = GETUNI("fade_end");
+
+	// misc. (custom shaders)
+	shader->uniforms[uniform_leveltime]   = GETUNI("leveltime");
+
+#undef GETUNI
+
+#ifdef HAVE_GLES2
+
+#define GETATTRIB(attribute) pglGetAttribLocation(shader->program, attribute)
+
+	shader->attributes[attrib_position]     = GETATTRIB("a_position");
+	shader->attributes[attrib_texcoord]     = GETATTRIB("a_texcoord");
+	shader->attributes[attrib_normal]       = GETATTRIB("a_normal");
+	shader->attributes[attrib_colors]       = GETATTRIB("a_colors");
+	shader->attributes[attrib_fadetexcoord] = GETATTRIB("a_fadetexcoord");
+
+#undef GETATTRIB
+
+#endif
+
+	return true;
+}
+
+boolean Shader_Compile(void)
+{
+	GLint i;
+
+	if (!GLExtension_shaders)
+		return false;
+
+	CustomShaders[SHADER_DEFAULT].vertex = NULL;
+	CustomShaders[SHADER_DEFAULT].fragment = NULL;
+
+	for (i = 0; ShaderSources[i].vertex && ShaderSources[i].fragment; i++)
+	{
+		FShaderObject *shader, *usershader;
+		const GLchar *vert_shader = ShaderSources[i].vertex;
+		const GLchar *frag_shader = ShaderSources[i].fragment;
+
+		if (i >= HWR_MAXSHADERS)
+			break;
+
+		shader = &ShaderObjects[i];
+		usershader = &UserShaderObjects[i];
+
+		if (shader->program)
+			pglDeleteProgram(shader->program);
+		if (usershader->program)
+			pglDeleteProgram(usershader->program);
+
+		shader->program = 0;
+		usershader->program = 0;
+
+		if (!Shader_CompileProgram(shader, i, vert_shader, frag_shader))
+			shader->program = 0;
+
+		// Compile custom shader
+		if ((i == SHADER_DEFAULT) || !(CustomShaders[i].vertex || CustomShaders[i].fragment))
+			continue;
+
+		// 18032019
+		if (CustomShaders[i].vertex)
+			vert_shader = CustomShaders[i].vertex;
+		if (CustomShaders[i].fragment)
+			frag_shader = CustomShaders[i].fragment;
+
+		if (!Shader_CompileProgram(usershader, i, vert_shader, frag_shader))
+		{
+			GL_MSG_Warning("Shader_Compile: Could not compile custom shader program for %s\n", HWR_GetShaderName(i));
+			usershader->program = 0;
+		}
+	}
+
+	Shader_Set(SHADER_DEFAULT);
+
+#ifdef HAVE_GLES2
+	pglUseProgram(ShaderState.program);
+	ShaderState.changed = false;
+#endif
+
+	return true;
+}
+
+static void Shader_SetIfChanged(FShaderObject *shader)
+{
+	if (ShaderState.changed)
+	{
+		pglUseProgram(shader->program);
+		ShaderState.changed = false;
+	}
+}
+
+#ifdef HAVE_GLES2
+void Shader_SetTransform(void)
+{
+	FShaderObject *shader = ShaderState.current;
+	if (!shader)
+		return;
+
+	Shader_SetIfChanged(shader);
+
+	if (memcmp(projMatrix, shader->projMatrix, sizeof(fmatrix4_t)))
+	{
+		memcpy(shader->projMatrix, projMatrix, sizeof(fmatrix4_t));
+		pglUniformMatrix4fv(shader->uniforms[uniform_projection], 1, GL_FALSE, (float *)projMatrix);
+	}
+
+	if (memcmp(viewMatrix, shader->viewMatrix, sizeof(fmatrix4_t)))
+	{
+		memcpy(shader->viewMatrix, viewMatrix, sizeof(fmatrix4_t));
+		pglUniformMatrix4fv(shader->uniforms[uniform_view], 1, GL_FALSE, (float *)viewMatrix);
+	}
+
+	if (memcmp(modelMatrix, shader->modelMatrix, sizeof(fmatrix4_t)))
+	{
+		memcpy(shader->modelMatrix, modelMatrix, sizeof(fmatrix4_t));
+		pglUniformMatrix4fv(shader->uniforms[uniform_model], 1, GL_FALSE, (float *)modelMatrix);
+	}
+}
+#endif
+
+void Shader_SetUniforms(FSurfaceInfo *Surface, GLRGBAFloat *poly, GLRGBAFloat *tint, GLRGBAFloat *fade)
+{
+	FShaderObject *shader = ShaderState.current;
+
+	if (ShadersEnabled && (shader != NULL) && pglUseProgram)
+	{
+		if (!shader->program)
+		{
+			pglUseProgram(0);
+			return;
+		}
+
+		Shader_SetIfChanged(shader);
+
+		// Color uniforms can be left NULL and will be set to white (1.0f, 1.0f, 1.0f, 1.0f)
+		if (poly == NULL)
+			poly = &ShaderDefaultColor;
+		if (tint == NULL)
+			tint = &ShaderDefaultColor;
+		if (fade == NULL)
+			fade = &ShaderDefaultColor;
+
+		#define UNIFORM_1(uniform, a, function) \
+			if (uniform != -1) \
+				function (uniform, a);
+
+		#define UNIFORM_2(uniform, a, b, function) \
+			if (uniform != -1) \
+				function (uniform, a, b);
+
+		#define UNIFORM_3(uniform, a, b, c, function) \
+			if (uniform != -1) \
+				function (uniform, a, b, c);
+
+		#define UNIFORM_4(uniform, a, b, c, d, function) \
+			if (uniform != -1) \
+				function (uniform, a, b, c, d);
+
+		// polygon
+		UNIFORM_4(shader->uniforms[uniform_poly_color], poly->red, poly->green, poly->blue, poly->alpha, pglUniform4f);
+		UNIFORM_4(shader->uniforms[uniform_tint_color], tint->red, tint->green, tint->blue, tint->alpha, pglUniform4f);
+		UNIFORM_4(shader->uniforms[uniform_fade_color], fade->red, fade->green, fade->blue, fade->alpha, pglUniform4f);
+
+		if (Surface != NULL)
+		{
+			UNIFORM_1(shader->uniforms[uniform_lighting], Surface->LightInfo.LightLevel, pglUniform1f);
+			UNIFORM_1(shader->uniforms[uniform_fade_start], Surface->LightInfo.FadeStart, pglUniform1f);
+			UNIFORM_1(shader->uniforms[uniform_fade_end], Surface->LightInfo.FadeEnd, pglUniform1f);
+		}
+
+		UNIFORM_1(shader->uniforms[uniform_leveltime], ((float)ShaderLevelTime) / TICRATE, pglUniform1f);
+
+		#undef UNIFORM_1
+		#undef UNIFORM_2
+		#undef UNIFORM_3
+		#undef UNIFORM_4
+	}
+}
+
+void Shader_SetSampler(EShaderUniform uniform, GLint value)
+{
+	FShaderObject *shader = ShaderState.current;
+	if (!shader)
+		return;
+
+	Shader_SetIfChanged(shader);
+
+	if (shader->uniforms[uniform] != -1)
+		pglUniform1i(shader->uniforms[uniform], value);
+}
diff --git a/src/hardware/shaders/gl_shaders.h b/src/hardware/shaders/gl_shaders.h
new file mode 100644
index 0000000000000000000000000000000000000000..f925c291fa52196c9ec9093f202967526a774217
--- /dev/null
+++ b/src/hardware/shaders/gl_shaders.h
@@ -0,0 +1,159 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2020 by Jaime "Lactozilla" Passos.
+// Copyright (C) 1998-2020 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file gl_shaders.h
+/// \brief OpenGL shaders
+
+#ifndef _GL_SHADERS_H_
+#define _GL_SHADERS_H_
+
+#include "../../doomdef.h"
+#include "../hw_gpu.h"
+
+#include "../r_glcommon/r_glcommon.h"
+
+extern GLboolean ShadersEnabled;
+extern INT32 ShadersAllowed;
+
+#ifdef GL_SHADERS
+
+void Shader_LoadFunctions(void);
+
+enum
+{
+	LOC_POSITION  = 0,
+	LOC_TEXCOORD  = 1,
+	LOC_NORMAL    = 2,
+	LOC_COLORS    = 3,
+
+	LOC_TEXCOORD0 = LOC_TEXCOORD,
+	LOC_TEXCOORD1 = 4
+};
+
+#define MAXSHADERS 16
+#define MAXSHADERPROGRAMS 16
+
+// 08072020
+typedef enum EShaderUniform
+{
+#ifdef HAVE_GLES2
+	// transform
+	uniform_model,
+	uniform_view,
+	uniform_projection,
+
+	// samplers
+	uniform_startscreen,
+	uniform_endscreen,
+	uniform_fademask,
+#endif
+
+	// lighting
+	uniform_poly_color,
+	uniform_tint_color,
+	uniform_fade_color,
+	uniform_lighting,
+	uniform_fade_start,
+	uniform_fade_end,
+
+	// misc.
+#ifdef HAVE_GLES2
+	uniform_isfadingin,
+	uniform_istowhite,
+#endif
+	uniform_leveltime,
+
+	uniform_max,
+} EShaderUniform;
+
+// 27072020
+#ifdef HAVE_GLES2
+typedef enum EShaderAttribute
+{
+	attrib_position,     // LOC_POSITION
+	attrib_texcoord,     // LOC_TEXCOORD + LOC_TEXCOORD0
+	attrib_normal,       // LOC_NORMAL
+	attrib_colors,       // LOC_COLORS
+	attrib_fadetexcoord, // LOC_TEXCOORD1
+
+	attrib_max,
+} EShaderAttribute;
+#endif
+
+typedef struct FShaderObject
+{
+	GLuint program;
+	boolean custom;
+
+	GLint uniforms[uniform_max+1];
+#ifdef HAVE_GLES2
+	GLint attributes[attribute_max+1];
+#endif
+
+#ifdef HAVE_GLES2
+	fmatrix4_t projMatrix;
+	fmatrix4_t viewMatrix;
+	fmatrix4_t modelMatrix;
+#endif
+} FShaderObject;
+
+extern FShaderObject ShaderObjects[HWR_MAXSHADERS];
+extern FShaderObject UserShaderObjects[HWR_MAXSHADERS];
+extern FShaderSource CustomShaders[HWR_MAXSHADERS];
+
+// 09102020
+typedef struct FShaderState
+{
+	FShaderObject *current;
+	GLuint type;
+	GLuint program;
+	boolean changed;
+} FShaderState;
+extern FShaderState ShaderState;
+
+void Shader_Set(int type);
+void Shader_UnSet(void);
+
+#ifdef HAVE_GLES2
+void Shader_SetTransform(void);
+#endif
+
+void Shader_LoadCustom(int number, char *code, size_t size, boolean isfragment);
+
+boolean Shader_Compile(void);
+void Shader_Clean(void);
+
+void Shader_SetUniforms(FSurfaceInfo *Surface, GLRGBAFloat *poly, GLRGBAFloat *tint, GLRGBAFloat *fade);
+void Shader_SetSampler(EShaderUniform uniform, GLint value);
+#define Shader_SetIntegerUniform Shader_SetSampler
+void Shader_SetInfo(INT32 info, INT32 value);
+
+#ifdef HAVE_GLES2
+int Shader_AttribLoc(int loc);
+#endif
+
+#ifndef GL_FRAGMENT_SHADER
+#define GL_FRAGMENT_SHADER 0x8B30
+#endif
+#ifndef GL_VERTEX_SHADER
+#define GL_VERTEX_SHADER 0x8B31
+#endif
+#ifndef GL_COMPILE_STATUS
+#define GL_COMPILE_STATUS 0x8B81
+#endif
+#ifndef GL_LINK_STATUS
+#define GL_LINK_STATUS 0x8B82
+#endif
+#ifndef GL_INFO_LOG_LENGTH
+#define GL_INFO_LOG_LENGTH 0x8B84
+#endif
+
+#endif // GL_SHADERS
+
+#endif // _GL_SHADERS_H_
diff --git a/src/hardware/shaders/shaders_gl2.h b/src/hardware/shaders/shaders_gl2.h
new file mode 100644
index 0000000000000000000000000000000000000000..e7da80acd1c1a9ae466d3e73dabdfbd2f34ae42e
--- /dev/null
+++ b/src/hardware/shaders/shaders_gl2.h
@@ -0,0 +1,149 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2020 by Jaime "Lactozilla" Passos.
+// Copyright (C) 1998-2020 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file shaders_gl2.h
+/// \brief OpenGL shader definitions
+
+#include "gl_shaders.h"
+
+// ================
+//  Vertex shaders
+// ================
+
+//
+// Generic vertex shader
+//
+
+#define GLSL_DEFAULT_VERTEX_SHADER \
+	"void main()\n" \
+	"{\n" \
+		"gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \
+		"gl_FrontColor = gl_Color;\n" \
+		"gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;\n" \
+		"gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" \
+	"}\0"
+
+// ==================
+//  Fragment shaders
+// ==================
+
+//
+// Generic fragment shader
+//
+
+#define GLSL_DEFAULT_FRAGMENT_SHADER \
+	"uniform sampler2D tex;\n" \
+	"uniform vec4 poly_color;\n" \
+	"void main(void) {\n" \
+		"gl_FragColor = texture2D(tex, gl_TexCoord[0].st) * poly_color;\n" \
+	"}\0"
+
+// replicates the way fixed function lighting is used by the model lighting option,
+// stores the lighting result to gl_Color
+// (ambient lighting of 0.75 and diffuse lighting from above)
+#define GLSL_MODEL_LIGHTING_VERTEX_SHADER \
+	"void main()\n" \
+	"{\n" \
+		"float nDotVP = dot(gl_Normal, vec3(0, 1, 0));\n" \
+		"float light = 0.75 + max(nDotVP, 0.0);\n" \
+		"gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n" \
+		"gl_FrontColor = vec4(light, light, light, 1.0);\n" \
+		"gl_TexCoord[0].xy = gl_MultiTexCoord0.xy;\n" \
+		"gl_ClipVertex = gl_ModelViewMatrix * gl_Vertex;\n" \
+	"}\0"
+
+//
+// Software fragment shader
+//
+
+#include "shaders_software.h"
+
+#define GLSL_SOFTWARE_FRAGMENT_SHADER \
+	"uniform sampler2D tex;\n" \
+	GLSL_DOOM_UNIFORMS \
+	GLSL_DOOM_COLORMAP \
+	GLSL_DOOM_LIGHT_EQUATION \
+	"void main(void) {\n" \
+		"vec4 Texel = texture2D(tex, gl_TexCoord[0].st);\n" \
+		"vec4 BaseColor = Texel * poly_color;\n" \
+		"vec4 FinalColor = BaseColor;\n" \
+		GLSL_SOFTWARE_TINT_EQUATION \
+		GLSL_SOFTWARE_FADE_EQUATION \
+		"FinalColor.a = Texel.a * poly_color.a;\n" \
+		"gl_FragColor = FinalColor;\n" \
+	"}\0"
+
+// same as above but multiplies results with the lighting value from the
+// accompanying vertex shader (stored in gl_Color)
+#define GLSL_SOFTWARE_MODEL_LIGHTING_FRAGMENT_SHADER \
+	"uniform sampler2D tex;\n" \
+	GLSL_DOOM_UNIFORMS \
+	GLSL_DOOM_COLORMAP \
+	GLSL_DOOM_LIGHT_EQUATION \
+	"void main(void) {\n" \
+		"vec4 Texel = texture2D(tex, gl_TexCoord[0].st);\n" \
+		"vec4 BaseColor = Texel * poly_color;\n" \
+		"vec4 FinalColor = BaseColor;\n" \
+		GLSL_SOFTWARE_TINT_EQUATION \
+		GLSL_SOFTWARE_FADE_EQUATION \
+		"FinalColor *= gl_Color;\n" \
+		"FinalColor.a = Texel.a * poly_color.a;\n" \
+		"gl_FragColor = FinalColor;\n" \
+	"}\0"
+
+//
+// Water surface shader
+//
+
+#define GLSL_WATER_FRAGMENT_SHADER \
+	"uniform sampler2D tex;\n" \
+	GLSL_DOOM_UNIFORMS \
+	GLSL_WATER_VARIABLES \
+	GLSL_DOOM_COLORMAP \
+	GLSL_DOOM_LIGHT_EQUATION \
+	"void main(void) {\n" \
+		GLSL_WATER_DISTORT \
+		"vec4 WaterTexel = texture2D(tex, vec2(gl_TexCoord[0].s - sdistort, gl_TexCoord[0].t - cdistort));\n" \
+		GLSL_WATER_MIX \
+		"gl_FragColor = FinalColor;\n" \
+	"}\0"
+
+//
+// Sky fragment shader
+// Modulates poly_color with gl_Color
+//
+
+#define GLSL_SKY_FRAGMENT_SHADER \
+	"uniform sampler2D tex;\n" \
+	"uniform vec4 poly_color;\n" \
+	"void main(void) {\n" \
+		"gl_FragColor = texture2D(tex, gl_TexCoord[0].st) * gl_Color * poly_color;\n" \
+	"}\0"
+
+//
+// Fog block shader
+//
+// Alpha of the planes themselves are still slightly off -- see HWR_FogBlockAlpha
+//
+
+#define GLSL_FOG_FRAGMENT_SHADER \
+	"uniform vec4 tint_color;\n" \
+	"uniform vec4 fade_color;\n" \
+	"uniform float lighting;\n" \
+	"uniform float fade_start;\n" \
+	"uniform float fade_end;\n" \
+	GLSL_DOOM_COLORMAP \
+	GLSL_DOOM_LIGHT_EQUATION \
+	"void main(void) {\n" \
+		"vec4 BaseColor = gl_Color;\n" \
+		"vec4 FinalColor = BaseColor;\n" \
+		GLSL_SOFTWARE_TINT_EQUATION \
+		GLSL_SOFTWARE_FADE_EQUATION \
+		"gl_FragColor = FinalColor;\n" \
+	"}\0"
diff --git a/src/hardware/shaders/shaders_gles2.h b/src/hardware/shaders/shaders_gles2.h
new file mode 100644
index 0000000000000000000000000000000000000000..3b2e99ff36fd19cae75979bd69f4e1ae7a29e4f0
--- /dev/null
+++ b/src/hardware/shaders/shaders_gles2.h
@@ -0,0 +1,241 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2020 by Jaime "Lactozilla" Passos.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file shaders_gles2.h
+/// \brief OpenGL ES shader definitions
+
+#include "gl_shaders.h"
+
+#define GLSL_BASE_VARYING \
+	"varying vec2 v_texcoord;\n" \
+	"varying vec3 v_normal;\n" \
+	"varying vec4 v_colors;\n"
+
+// ================
+//  Vertex shaders
+// ================
+
+#define GLSL_DEFAULT_VERTEX_SHADER \
+	"attribute vec3 a_position;\n" \
+	"attribute vec2 a_texcoord;\n" \
+	"attribute vec3 a_normal;\n" \
+	"attribute vec4 a_colors;\n" \
+	GLSL_BASE_VARYING \
+	"uniform mat4 u_model;\n" \
+	"uniform mat4 u_view;\n" \
+	"uniform mat4 u_projection;\n" \
+	"void main()\n" \
+	"{\n" \
+		"gl_Position = u_projection * u_view * u_model * vec4(a_position, 1.0f);\n" \
+		"v_texcoord = vec2(a_texcoord.x, a_texcoord.y);\n" \
+		"v_normal = a_normal;\n" \
+		"v_colors = a_colors;\n" \
+	"}\0"
+
+//
+// Fade mask vertex shader
+//
+
+#define GLSL_FADEMASK_VERTEX_SHADER \
+	"attribute vec3 a_position;\n" \
+	"attribute vec2 a_texcoord;\n" \
+	"attribute vec2 a_fademasktexcoord;\n" \
+	"varying vec2 v_texcoord;\n" \
+	"varying vec3 v_fademasktexcoord;\n" \
+	"uniform mat4 u_projection;\n" \
+	"void main()\n" \
+	"{\n" \
+		"gl_Position = u_projection * vec4(a_position, 1.0f);\n" \
+		"v_texcoord = vec2(a_texcoord.x, a_texcoord.y);\n" \
+		"v_fademasktexcoord = vec2(a_fadetex.x, a_fadetex.y);\n" \
+	"}\0"
+
+// replicates the way fixed function lighting is used by the model lighting option,
+// stores the lighting result to gl_Color
+// (ambient lighting of 0.75 and diffuse lighting from above)
+#define GLSL_MODEL_LIGHTING_VERTEX_SHADER \
+	"attribute vec3 a_position;\n" \
+	"attribute vec2 a_texcoord;\n" \
+	"attribute vec3 a_normal;\n" \
+	"attribute vec4 a_colors;\n" \
+	"varying vec2 v_texcoord;\n" \
+	"varying vec3 v_normal;\n" \
+	"varying vec4 v_colors;\n" \
+	"uniform mat4 u_model;\n" \
+	"uniform mat4 u_view;\n" \
+	"uniform mat4 u_projection;\n" \
+	"void main()\n" \
+	"{\n" \
+		"float nDotVP = dot(a_normal, vec3(0, 1, 0));\n" \
+		"float light = 0.75 + max(nDotVP, 0.0);\n" \
+		"gl_Position = u_projection * vec4(a_position, 1.0f);\n" \
+		"v_texcoord = vec2(a_texcoord.x, a_texcoord.y);\n" \
+		"v_normal = a_normal;\n" \
+		"v_colors = vec4(light, light, light, 1.0) * a_colors;\n" \
+	"}\0"
+
+// ==================
+//  Fragment shaders
+// ==================
+
+#define GLSL_BASE_SAMPLER "uniform sampler2D t_texsampler;\n"
+#define GLSL_BASE_UNIFORMS \
+	GLSL_BASE_SAMPLER \
+	"uniform vec4 poly_color;\n" \
+
+#define GLSL_DEFAULT_FRAGMENT_SHADER \
+	GLSL_BASE_VARYING \
+	GLSL_BASE_UNIFORMS \
+	"void main(void) {\n" \
+		"gl_FragColor = texture2D(t_texsampler, v_texcoord) * poly_color;\n" \
+	"}\0"
+
+//
+// Sky fragment shader
+//
+
+#define GLSL_SKY_FRAGMENT_SHADER \
+	GLSL_BASE_VARYING \
+	GLSL_BASE_UNIFORMS \
+	"void main(void) {\n" \
+		"gl_FragColor = texture2D(t_texsampler, v_texcoord) * v_colors;\n" \
+	"}\0"
+
+//
+// Fade mask fragment shader
+//
+
+#define GLSL_FADEMASK_BASE_IN \
+	"varying vec2 v_texcoord;\n" \
+	"varying vec3 v_fademasktexcoord;\n" \
+	"uniform sampler2D t_startscreen;\n" \
+	"uniform sampler2D t_endscreen;\n" \
+	"uniform sampler2D t_fademask;\n"
+
+#define GLSL_FADEMASK_FRAGMENT_SHADER \
+	GLSL_FADEMASK_BASE_IN \
+	"void main(void) {\n" \
+		"vec4 StartTexel = texture2D(t_startscreen, v_texcoord);\n" \
+		"vec4 EndTexel = texture2D(t_endscreen, v_texcoord);\n" \
+		"vec4 MaskTexel = texture2D(t_fademask, v_fademasktexcoord);\n" \
+		"gl_FragColor = mix(StartTexel, EndTexel, MaskTexel.r);\n" \
+	"}\0"
+
+// Lactozilla: Very simple shader that uses either additive
+// or subtractive blending depending on the wipe style.
+#define GLSL_FADEMASK_ADDITIVEANDSUBTRACTIVE_FRAGMENT_SHADER \
+	GLSL_FADEMASK_BASE_IN \
+	"uniform bool is_fading_in;\n" \
+	"uniform bool is_to_white;\n" \
+	"void main(void) {\n" \
+		"vec4 MaskTexel = texture2D(t_fademask, v_fademasktexcoord);\n" \
+		"vec4 MixTexel;\n" \
+		"vec4 FinalColor;\n" \
+		"float FadeAlpha = MaskTexel.r;\n" \
+		"if (is_fading_in == true)\n" \
+		"{\n" \
+			"FadeAlpha = (1.0f - FadeAlpha);\n" \
+			"MixTexel = texture2D(t_endscreen, v_texcoord);\n" \
+		"}\n" \
+		"else\n" \
+			"MixTexel = texture2D(t_startscreen, v_texcoord);\n" \
+		"float FadeRed = clamp((FadeAlpha * 3.0f), 0.0f, 1.0f);\n" \
+		"float FadeGreen = clamp((FadeAlpha * 2.0f), 0.0f, 1.0f);\n" \
+		"if (is_to_white == true)\n" \
+		"{\n" \
+			"FinalColor.r = MixTexel.r + FadeRed;\n" \
+			"FinalColor.g = MixTexel.g + FadeGreen;\n" \
+			"FinalColor.b = MixTexel.b + FadeAlpha;\n" \
+		"}\n" \
+		"else\n" \
+		"{\n" \
+			"FinalColor.r = MixTexel.r - FadeRed;\n" \
+			"FinalColor.g = MixTexel.g - FadeGreen;\n" \
+			"FinalColor.b = MixTexel.b - FadeAlpha;\n" \
+		"}\n" \
+		"FinalColor.a = 1.0f;\n" \
+		"gl_FragColor = FinalColor;\n" \
+	"}\0"
+
+//
+// Software fragment shader
+//
+
+#include "shaders_software.h"
+
+#define GLSL_SOFTWARE_FRAGMENT_SHADER \
+	GLSL_BASE_VARYING \
+	GLSL_BASE_SAMPLER \
+	GLSL_DOOM_UNIFORMS \
+	GLSL_DOOM_COLORMAP \
+	GLSL_DOOM_LIGHT_EQUATION \
+	"void main(void) {\n" \
+		"vec4 Texel = texture2D(t_texsampler, v_texcoord);\n" \
+		"vec4 BaseColor = Texel * poly_color;\n" \
+		"vec4 FinalColor = BaseColor;\n" \
+		GLSL_SOFTWARE_TINT_EQUATION \
+		GLSL_SOFTWARE_FADE_EQUATION \
+		"FinalColor.a = Texel.a * poly_color.a;\n" \
+		"gl_FragColor = FinalColor;\n" \
+	"}\0"
+
+// same as above but multiplies results with the lighting value from the
+// accompanying vertex shader (stored in gl_Color)
+#define GLSL_SOFTWARE_MODEL_LIGHTING_FRAGMENT_SHADER \
+	GLSL_BASE_VARYING \
+	GLSL_BASE_SAMPLER \
+	GLSL_DOOM_UNIFORMS \
+	GLSL_DOOM_COLORMAP \
+	GLSL_DOOM_LIGHT_EQUATION \
+	"void main(void) {\n" \
+		"vec4 Texel = texture2D(t_texsampler, v_texcoord);\n" \
+		"vec4 BaseColor = Texel * poly_color;\n" \
+		"vec4 FinalColor = BaseColor;\n" \
+		GLSL_SOFTWARE_TINT_EQUATION \
+		GLSL_SOFTWARE_FADE_EQUATION \
+		"FinalColor *= v_colors;\n" \
+		"FinalColor.a = Texel.a * poly_color.a;\n" \
+		"gl_FragColor = FinalColor;\n" \
+	"}\0"
+
+//
+// Water surface shader
+//
+
+#define GLSL_WATER_FRAGMENT_SHADER \
+	GLSL_BASE_VARYING \
+	GLSL_BASE_SAMPLER \
+	GLSL_DOOM_UNIFORMS \
+	GLSL_WATER_VARIABLES \
+	GLSL_DOOM_COLORMAP \
+	GLSL_DOOM_LIGHT_EQUATION \
+	"void main(void) {\n" \
+		GLSL_WATER_DISTORT \
+		"vec4 WaterTexel = texture2D(t_texsampler, vec2(v_texcoord.s - sdistort, v_texcoord.t - cdistort));\n" \
+		GLSL_WATER_MIX \
+		"gl_FragColor = FinalColor;\n" \
+	"}\0"
+
+//
+// Fog block shader
+//
+// Alpha of the planes themselves are still slightly off -- see HWR_FogBlockAlpha
+//
+
+#define GLSL_FOG_FRAGMENT_SHADER \
+	"varying vec2 v_texcoord;\n" \
+	GLSL_DOOM_UNIFORMS \
+	GLSL_DOOM_COLORMAP \
+	GLSL_DOOM_LIGHT_EQUATION \
+	"void main(void) {\n" \
+		"vec4 BaseColor = poly_color;\n" \
+		"vec4 FinalColor = BaseColor;\n" \
+		GLSL_SOFTWARE_TINT_EQUATION \
+		GLSL_SOFTWARE_FADE_EQUATION \
+		"gl_FragColor = FinalColor;\n" \
+	"}\0"
diff --git a/src/hardware/shaders/shaders_software.h b/src/hardware/shaders/shaders_software.h
new file mode 100644
index 0000000000000000000000000000000000000000..8cbfc2d4ca11430e55490385055a6ef33553d8a5
--- /dev/null
+++ b/src/hardware/shaders/shaders_software.h
@@ -0,0 +1,83 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2020 by Jaime "Lactozilla" Passos.
+// Copyright (C) 1998-2020 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file shaders_software.h
+/// \brief Software fragment shader
+
+#define GLSL_DOOM_UNIFORMS \
+	"uniform vec4 poly_color;\n" \
+	"uniform vec4 tint_color;\n" \
+	"uniform vec4 fade_color;\n" \
+	"uniform float lighting;\n" \
+	"uniform float fade_start;\n" \
+	"uniform float fade_end;\n"
+
+#define GLSL_DOOM_COLORMAP \
+	"float R_DoomColormap(float light, float z)\n" \
+	"{\n" \
+		"float lightnum = clamp(light / 17.0, 0.0, 15.0);\n" \
+		"float lightz = clamp(z / 16.0, 0.0, 127.0);\n" \
+		"float startmap = (15.0 - lightnum) * 4.0;\n" \
+		"float scale = 160.0 / (lightz + 1.0);\n" \
+		"return startmap - scale * 0.5;\n" \
+	"}\n"
+
+#define GLSL_DOOM_LIGHT_EQUATION \
+	"float R_DoomLightingEquation(float light)\n" \
+	"{\n" \
+		"float z = gl_FragCoord.z / gl_FragCoord.w;\n" \
+		"float colormap = floor(R_DoomColormap(light, z)) + 0.5;\n" \
+		"return clamp(colormap, 0.0, 31.0) / 32.0;\n" \
+	"}\n"
+
+#define GLSL_SOFTWARE_TINT_EQUATION \
+	"if (tint_color.a > 0.0) {\n" \
+		"float bright = sqrt((BaseColor.r * BaseColor.r) + (BaseColor.g * BaseColor.g) + (BaseColor.b * BaseColor.b));\n" \
+		"float strength = sqrt(9.0 * tint_color.a);\n" \
+		"FinalColor.r = clamp((bright * (tint_color.r * strength)) + (BaseColor.r * (1.0 - strength)), 0.0, 1.0);\n" \
+		"FinalColor.g = clamp((bright * (tint_color.g * strength)) + (BaseColor.g * (1.0 - strength)), 0.0, 1.0);\n" \
+		"FinalColor.b = clamp((bright * (tint_color.b * strength)) + (BaseColor.b * (1.0 - strength)), 0.0, 1.0);\n" \
+	"}\n"
+
+#define GLSL_SOFTWARE_FADE_EQUATION \
+	"float darkness = R_DoomLightingEquation(lighting);\n" \
+	"if (fade_start != 0.0 || fade_end != 31.0) {\n" \
+		"float fs = fade_start / 31.0;\n" \
+		"float fe = fade_end / 31.0;\n" \
+		"float fd = fe - fs;\n" \
+		"darkness = clamp((darkness - fs) * (1.0 / fd), 0.0, 1.0);\n" \
+	"}\n" \
+	"FinalColor = mix(FinalColor, fade_color, darkness);\n"
+
+//
+// Water surface shader
+//
+// Mostly guesstimated, rather than the rest being built off Software science.
+// Still needs to distort things underneath/around the water...
+//
+
+#define GLSL_WATER_VARIABLES \
+	"uniform float leveltime;\n" \
+	"const float freq = 0.025;\n" \
+	"const float amp = 0.025;\n" \
+	"const float speed = 2.0;\n" \
+	"const float pi = 3.14159;\n"
+
+#define GLSL_WATER_DISTORT \
+	"float z = (gl_FragCoord.z / gl_FragCoord.w) / 2.0;\n" \
+	"float a = -pi * (z * freq) + (leveltime * speed);\n" \
+	"float sdistort = sin(a) * amp;\n" \
+	"float cdistort = cos(a) * amp;\n"
+
+#define GLSL_WATER_MIX \
+	"vec4 BaseColor = WaterTexel * poly_color;\n" \
+	"vec4 FinalColor = BaseColor;\n" \
+	GLSL_SOFTWARE_TINT_EQUATION \
+	GLSL_SOFTWARE_FADE_EQUATION \
+	"FinalColor.a = WaterTexel.a * poly_color.a;\n"
diff --git a/src/sdl/Makefile.cfg b/src/sdl/Makefile.cfg
index 45d0d6ba75a666cba5e4e2c3a3f9704987705cb6..86957caaffc06e7acd16b8de8a12f57f80fe6b7d 100644
--- a/src/sdl/Makefile.cfg
+++ b/src/sdl/Makefile.cfg
@@ -50,7 +50,7 @@ endif
 	OPTS+=-DDIRECTFULLSCREEN -DHAVE_SDL
 
 ifndef NOHW
-	OBJS+=$(OBJDIR)/r_opengl.o $(OBJDIR)/ogl_sdl.o
+	OBJS+=$(OBJDIR)/r_opengl.o $(OBJDIR)/r_glcommon.o $(OBJDIR)/gl_shaders.o $(OBJDIR)/ogl_sdl.o
 endif
 
 ifdef NOMIXER
diff --git a/src/sdl/hwsym_sdl.c b/src/sdl/hwsym_sdl.c
index 7dcf91823be97d3001030cb7abbc004fda7fcb90..f790d544b25162ddeb67eff8dee87c8be242d0e6 100644
--- a/src/sdl/hwsym_sdl.c
+++ b/src/sdl/hwsym_sdl.c
@@ -61,10 +61,11 @@
 
 	\return	void
 */
-//
+
 void *hwSym(const char *funcName,void *handle)
 {
 	void *funcPointer = NULL;
+	if (0) ;
 #ifdef STATIC3DS
 	GETFUNC(Startup);
 	GETFUNC(AddSfx);
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 2eb7c7496ab85491b42bfd4caa36c442e3f45bfa..03e460d0861bea66ddfe51ec5a00a0f08b22df34 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -75,9 +75,11 @@
 #include "../r_main.h"
 #include "../lua_hook.h"
 #include "sdlmain.h"
+
 #ifdef HWRENDER
 #include "../hardware/hw_main.h"
 #include "../hardware/hw_gpu.h"
+#include "../hardware/r_glcommon/r_glcommon.h"
 // For dynamic referencing of HW rendering functions
 #include "hwsym_sdl.h"
 #include "ogl_sdl.h"
@@ -1477,20 +1479,23 @@ static SDL_bool Impl_CreateContext(void)
 
 void VID_CheckGLLoaded(rendermode_t oldrender)
 {
-	(void)oldrender;
 #ifdef HWRENDER
-	if (vid.glstate == VID_GL_LIBRARY_ERROR) // Well, it didn't work the first time anyway.
-	{
-		CONS_Alert(CONS_ERROR, "OpenGL never loaded\n");
+	if (vid.glstate != VID_GL_LIBRARY_ERROR)
+		return;
+
+	// Well, it didn't work the first time anyway.
+	if (chosenrendermode == render_opengl) // fallback to software
+		rendermode = render_soft;
+	else
 		rendermode = oldrender;
-		if (chosenrendermode == render_opengl) // fallback to software
-			rendermode = render_soft;
-		if (setrenderneeded)
-		{
-			CV_StealthSetValue(&cv_renderer, oldrender);
-			setrenderneeded = 0;
-		}
+
+	if (setrenderneeded)
+	{
+		CV_StealthSetValue(&cv_renderer, oldrender);
+		setrenderneeded = 0;
 	}
+#else
+	(void)oldrender;
 #endif
 }
 
@@ -1854,7 +1859,7 @@ void VID_StartupOpenGL(void)
 
 		GPUInterface_Load(&GPU);
 
-		if (VID_LoadGPUAPI() && GPU->Init())
+		if (GLBackend_LoadLibrary() && GPU->Init())
 			vid.glstate = VID_GL_LIBRARY_LOADED;
 		else
 		{
diff --git a/src/sdl/ogl_sdl.c b/src/sdl/ogl_sdl.c
index 2c4ee341497019dee544b981e0b037b2527176e6..0df9fc051d603591839d852d0267c358381bf68b 100644
--- a/src/sdl/ogl_sdl.c
+++ b/src/sdl/ogl_sdl.c
@@ -72,7 +72,7 @@ PFNglGetString pglGetString;
 void *GLUhandle = NULL;
 SDL_GLContext sdlglcontext = 0;
 
-void *GetGLFunc(const char *proc)
+void *GLBackend_GetFunction(const char *proc)
 {
 	if (strncmp(proc, "glu", 3) == 0)
 	{
@@ -84,7 +84,7 @@ void *GetGLFunc(const char *proc)
 	return SDL_GL_GetProcAddress(proc);
 }
 
-boolean VID_LoadGPUAPI(void)
+boolean GLBackend_LoadLibrary(void)
 {
 #ifndef STATIC_OPENGL
 	const char *OGLLibname = NULL;
@@ -99,7 +99,7 @@ boolean VID_LoadGPUAPI(void)
 					"Falling back to Software mode.\n", SDL_GetError());
 		if (!M_CheckParm("-OGLlib"))
 			CONS_Printf("If you know what is the OpenGL library's name, use -OGLlib\n");
-		return 0;
+		return false;
 	}
 
 #if 0
@@ -154,47 +154,16 @@ boolean VID_LoadGPUAPI(void)
 boolean OglSdlSurface(INT32 w, INT32 h)
 {
 	INT32 cbpp = cv_scr_depth.value < 16 ? 16 : cv_scr_depth.value;
-	static boolean first_init = false;
 
-	if (!first_init)
-	{
-		GLVersion = pglGetString(GL_VERSION);
-		GLRenderer = pglGetString(GL_RENDERER);
-		GLExtensions = pglGetString(GL_EXTENSIONS);
-
-		GL_DBG_Printf("OpenGL %s\n", GLVersion);
-		GL_DBG_Printf("GPU: %s\n", GLRenderer);
-		GL_DBG_Printf("Extensions: %s\n", GLExtensions);
+	GLBackend_InitContext();
+	GLBackend_LoadContextFunctions();
 
-		if (strcmp((const char*)GLRenderer, "GDI Generic") == 0 &&
-			strcmp((const char*)GLVersion, "1.1.0") == 0)
-		{
-			// Oh no... Windows gave us the GDI Generic rasterizer, so something is wrong...
-			// The game will crash later on when unsupported OpenGL commands are encountered.
-			// Instead of a nondescript crash, show a more informative error message.
-			// Also set the renderer variable back to software so the next launch won't
-			// repeat this error.
-			CV_StealthSet(&cv_renderer, "Software");
-			I_Error("OpenGL Error: Failed to access the GPU. There may be an issue with your graphics drivers.");
-		}
-	}
-	first_init = true;
-
-	if (isExtAvailable("GL_EXT_texture_filter_anisotropic", GLExtensions))
-		pglGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &GPUMaximumAnisotropy);
-	else
-		GPUMaximumAnisotropy = 1;
-
-	SetupGLFunc4();
+	SetSurface(w, h);
 
 	glanisotropicmode_cons_t[1].value = GPUMaximumAnisotropy;
 
 	SDL_GL_SetSwapInterval(cv_vidwait.value ? 1 : 0);
 
-	SetModelView(w, h);
-	SetStates();
-	pglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
-
 	HWR_Startup();
 	GPUTextureFormat = cbpp > 16 ? GL_RGBA : GL_RGB5_A1;
 
@@ -224,7 +193,7 @@ void OglSdlFinishUpdate(boolean waitvbl)
 	HWR_DrawScreenFinalTexture(sdlw, sdlh);
 	SDL_GL_SwapWindow(window);
 
-	GClipRect(0, 0, realwidth, realheight, NZCLIP_PLANE);
+	GPU->GClipRect(0, 0, realwidth, realheight, NZCLIP_PLANE);
 
 	// Sryder:	We need to draw the final screen texture again into the other buffer in the original position so that
 	//			effects that want to take the old screen can do so after this