diff --git a/src/ClassiCube.vcxproj b/src/ClassiCube.vcxproj
index af90e9d24..cb1e96ad7 100644
--- a/src/ClassiCube.vcxproj
+++ b/src/ClassiCube.vcxproj
@@ -250,6 +250,7 @@
+
@@ -259,7 +260,7 @@
-
+
@@ -275,6 +276,7 @@
+
diff --git a/src/ClassiCube.vcxproj.filters b/src/ClassiCube.vcxproj.filters
index 4f8c6cb9f..f8d335020 100644
--- a/src/ClassiCube.vcxproj.filters
+++ b/src/ClassiCube.vcxproj.filters
@@ -327,6 +327,9 @@
Header Files\Platform
+
+ Header Files\Graphics
+
@@ -554,9 +557,6 @@
Source Files\Graphics
-
- Source Files\Graphics
-
Source Files\Network
@@ -587,5 +587,11 @@
Source Files\Platform
+
+ Source Files\Graphics
+
+
+ Source Files\Graphics
+
\ No newline at end of file
diff --git a/src/Graphics_GL.c b/src/Graphics_GL.c
deleted file mode 100644
index 8be8c00a1..000000000
--- a/src/Graphics_GL.c
+++ /dev/null
@@ -1,1207 +0,0 @@
-#include "Core.h"
-#ifdef CC_BUILD_GL
-#include "_GraphicsBase.h"
-#include "Chat.h"
-#include "Errors.h"
-#include "Logger.h"
-#include "Window.h"
-/* The OpenGL backend is a bit of a mess, since it's really 3 backends in one:
- * - OpenGL 1.1 (completely lacking GPU, fallbacks to say Windows built-in software rasteriser)
- * - OpenGL 1.5 or OpenGL 1.2 + GL_ARB_vertex_buffer_object (default desktop backend)
- * - OpenGL 2.0 (alternative modern-ish backend)
-*/
-
-#if defined CC_BUILD_WIN
-/* Avoid pointless includes */
-#define WIN32_LEAN_AND_MEAN
-#define NOSERVICE
-#define NOMCX
-#define NOIME
-#include
-#include
-#elif defined CC_BUILD_IOS
-#include
-#elif defined CC_BUILD_MACOS
-#include
-#elif defined CC_BUILD_GLES && defined CC_BUILD_GLMODERN
-#include
-#elif defined CC_BUILD_GLES
-#include
-#else
-#define GL_GLEXT_PROTOTYPES
-#include
-#endif
-
-/* Not present in gl.h on Windows (only up to OpenGL 1.1) */
-#define _GL_ARRAY_BUFFER 0x8892
-#define _GL_ELEMENT_ARRAY_BUFFER 0x8893
-#define _GL_STATIC_DRAW 0x88E4
-#define _GL_DYNAMIC_DRAW 0x88E8
-#define _GL_TEXTURE_MAX_LEVEL 0x813D
-
-#define _GL_FRAGMENT_SHADER 0x8B30
-#define _GL_VERTEX_SHADER 0x8B31
-#define _GL_COMPILE_STATUS 0x8B81
-#define _GL_LINK_STATUS 0x8B82
-#define _GL_INFO_LOG_LENGTH 0x8B84
-
-#if defined CC_BUILD_GL11
-static GLuint activeList;
-#define gl_DYNAMICLISTID 1234567891
-static void* dynamicListData;
-static cc_uint16 gl_indices[GFX_MAX_INDICES];
-#elif defined CC_BUILD_GLMODERN
-#define _glBindBuffer(t,b) glBindBuffer(t,b)
-#define _glDeleteBuffers(n,b) glDeleteBuffers(n,b)
-#define _glGenBuffers(n,b) glGenBuffers(n,b)
-#define _glBufferData(t,s,d,u) glBufferData(t,s,d,u)
-#define _glBufferSubData(t,o,s,d) glBufferSubData(t,o,s,d)
-#else
-/* OpenGL functions use stdcall instead of cdecl on Windows */
-#ifndef APIENTRY
-#define APIENTRY
-#endif
-static void (APIENTRY *_glBindBuffer)(GLenum target, GLuint buffer);
-static void (APIENTRY *_glDeleteBuffers)(GLsizei n, const GLuint *buffers);
-static void (APIENTRY *_glGenBuffers)(GLsizei n, GLuint *buffers);
-static void (APIENTRY *_glBufferData)(GLenum target, cc_uintptr size, const GLvoid* data, GLenum usage);
-static void (APIENTRY *_glBufferSubData)(GLenum target, cc_uintptr offset, cc_uintptr size, const GLvoid* data);
-#endif
-
-#if defined CC_BUILD_WEB || defined CC_BUILD_ANDROID
-#define PIXEL_FORMAT GL_RGBA
-#else
-#define PIXEL_FORMAT 0x80E1 /* GL_BGRA_EXT */
-#endif
-
-#if defined CC_BIG_ENDIAN
-/* Pixels are stored in memory as A,R,G,B but GL_UNSIGNED_BYTE will interpret as B,G,R,A */
-/* So use GL_UNSIGNED_INT_8_8_8_8_REV instead to remedy this */
-#define TRANSFER_FORMAT GL_UNSIGNED_INT_8_8_8_8_REV
-#else
-/* Pixels are stored in memory as B,G,R,A and GL_UNSIGNED_BYTE will interpret as B,G,R,A */
-/* So fine to just use GL_UNSIGNED_BYTE here */
-#define TRANSFER_FORMAT GL_UNSIGNED_BYTE
-#endif
-
-typedef void (*GL_SetupVBFunc)(void);
-typedef void (*GL_SetupVBRangeFunc)(int startVertex);
-static GL_SetupVBFunc gfx_setupVBFunc;
-static GL_SetupVBRangeFunc gfx_setupVBRangeFunc;
-/* Current format and size of vertices */
-static int gfx_stride, gfx_format = -1;
-
-static void GL_UpdateVsync(void) {
- GLContext_SetFpsLimit(gfx_vsync, gfx_minFrameMs);
-}
-
-static void GL_CheckSupport(void);
-void Gfx_Create(void) {
- GLContext_Create();
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &Gfx.MaxTexWidth);
- Gfx.MaxTexHeight = Gfx.MaxTexWidth;
- Gfx.Created = true;
-
- GL_CheckSupport();
- Gfx_RestoreState();
- GL_UpdateVsync();
-}
-
-cc_bool Gfx_TryRestoreContext(void) {
- return GLContext_TryRestore();
-}
-
-void Gfx_Free(void) {
- Gfx_FreeState();
- GLContext_Free();
-}
-
-#define gl_Toggle(cap) if (enabled) { glEnable(cap); } else { glDisable(cap); }
-static void* tmpData;
-static int tmpSize;
-
-static void* FastAllocTempMem(int size) {
- if (size > tmpSize) {
- Mem_Free(tmpData);
- tmpData = Mem_Alloc(size, 1, "Gfx_AllocTempMemory");
- }
-
- tmpSize = size;
- return tmpData;
-}
-
-
-/*########################################################################################################################*
-*---------------------------------------------------------Textures--------------------------------------------------------*
-*#########################################################################################################################*/
-static void Gfx_DoMipmaps(int x, int y, struct Bitmap* bmp, int rowWidth, cc_bool partial) {
- BitmapCol* prev = bmp->scan0;
- BitmapCol* cur;
-
- int lvls = CalcMipmapsLevels(bmp->width, bmp->height);
- int lvl, width = bmp->width, height = bmp->height;
-
- for (lvl = 1; lvl <= lvls; lvl++) {
- x /= 2; y /= 2;
- if (width > 1) width /= 2;
- if (height > 1) height /= 2;
-
- cur = (BitmapCol*)Mem_Alloc(width * height, 4, "mipmaps");
- GenMipmaps(width, height, cur, prev, rowWidth);
-
- if (partial) {
- glTexSubImage2D(GL_TEXTURE_2D, lvl, x, y, width, height, PIXEL_FORMAT, TRANSFER_FORMAT, cur);
- } else {
- glTexImage2D(GL_TEXTURE_2D, lvl, GL_RGBA, width, height, 0, PIXEL_FORMAT, TRANSFER_FORMAT, cur);
- }
-
- if (prev != bmp->scan0) Mem_Free(prev);
- prev = cur;
- rowWidth = width;
- }
- if (prev != bmp->scan0) Mem_Free(prev);
-}
-
-GfxResourceID Gfx_CreateTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) {
- GLuint texId;
- glGenTextures(1, &texId);
- glBindTexture(GL_TEXTURE_2D, texId);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
- if (!Math_IsPowOf2(bmp->width) || !Math_IsPowOf2(bmp->height)) {
- Logger_Abort("Textures must have power of two dimensions");
- }
- if (Gfx.LostContext) return 0;
-
- if (mipmaps) {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
- if (customMipmapsLevels) {
- int lvls = CalcMipmapsLevels(bmp->width, bmp->height);
- glTexParameteri(GL_TEXTURE_2D, _GL_TEXTURE_MAX_LEVEL, lvls);
- }
- } else {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- }
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bmp->width, bmp->height, 0, PIXEL_FORMAT, TRANSFER_FORMAT, bmp->scan0);
-
- if (mipmaps) Gfx_DoMipmaps(0, 0, bmp, bmp->width, false);
- return texId;
-}
-
-#define UPDATE_FAST_SIZE (64 * 64)
-static CC_NOINLINE void UpdateTextureSlow(int x, int y, struct Bitmap* part, int rowWidth) {
- BitmapCol buffer[UPDATE_FAST_SIZE];
- void* ptr = (void*)buffer;
- int count = part->width * part->height;
-
- /* cannot allocate memory on the stack for very big updates */
- if (count > UPDATE_FAST_SIZE) {
- ptr = Mem_Alloc(count, 4, "Gfx_UpdateTexture temp");
- }
-
- CopyTextureData(ptr, part->width << 2, part, rowWidth << 2);
- glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, part->width, part->height, PIXEL_FORMAT, TRANSFER_FORMAT, ptr);
- if (count > UPDATE_FAST_SIZE) Mem_Free(ptr);
-}
-
-void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, int rowWidth, cc_bool mipmaps) {
- glBindTexture(GL_TEXTURE_2D, (GLuint)texId);
- /* TODO: Use GL_UNPACK_ROW_LENGTH for Desktop OpenGL */
-
- if (part->width == rowWidth) {
- glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, part->width, part->height, PIXEL_FORMAT, TRANSFER_FORMAT, part->scan0);
- } else {
- UpdateTextureSlow(x, y, part, rowWidth);
- }
- if (mipmaps) Gfx_DoMipmaps(x, y, part, rowWidth, true);
-}
-
-void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) {
- Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps);
-}
-
-void Gfx_BindTexture(GfxResourceID texId) {
- glBindTexture(GL_TEXTURE_2D, (GLuint)texId);
-}
-
-void Gfx_DeleteTexture(GfxResourceID* texId) {
- GLuint id = (GLuint)(*texId);
- if (!id) return;
- glDeleteTextures(1, &id);
- *texId = 0;
-}
-
-void Gfx_EnableMipmaps(void) { }
-void Gfx_DisableMipmaps(void) { }
-
-
-/*########################################################################################################################*
-*-----------------------------------------------------State management----------------------------------------------------*
-*#########################################################################################################################*/
-static PackedCol gfx_clearColor, gfx_fogColor;
-static float gfx_fogEnd = -1.0f, gfx_fogDensity = -1.0f;
-static int gfx_fogMode = -1;
-
-void Gfx_SetFaceCulling(cc_bool enabled) { gl_Toggle(GL_CULL_FACE); }
-void Gfx_SetAlphaBlending(cc_bool enabled) { gl_Toggle(GL_BLEND); }
-void Gfx_SetAlphaArgBlend(cc_bool enabled) { }
-
-static void GL_ClearCol(PackedCol col) {
- glClearColor(PackedCol_R(col) / 255.0f, PackedCol_G(col) / 255.0f,
- PackedCol_B(col) / 255.0f, PackedCol_A(col) / 255.0f);
-}
-void Gfx_ClearCol(PackedCol col) {
- if (col == gfx_clearColor) return;
- GL_ClearCol(col);
- gfx_clearColor = col;
-}
-
-void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) {
- glColorMask(r, g, b, a);
-}
-
-void Gfx_SetDepthWrite(cc_bool enabled) { glDepthMask(enabled); }
-void Gfx_SetDepthTest(cc_bool enabled) { gl_Toggle(GL_DEPTH_TEST); }
-
-void Gfx_CalcOrthoMatrix(float width, float height, struct Matrix* matrix) {
- Matrix_Orthographic(matrix, 0.0f, width, 0.0f, height, ORTHO_NEAR, ORTHO_FAR);
-}
-void Gfx_CalcPerspectiveMatrix(float fov, float aspect, float zFar, struct Matrix* matrix) {
- float zNear = 0.1f;
- Matrix_PerspectiveFieldOfView(matrix, fov, aspect, zNear, zFar);
-}
-
-
-/*########################################################################################################################*
-*-------------------------------------------------------Index buffers-----------------------------------------------------*
-*#########################################################################################################################*/
-#ifndef CC_BUILD_GL11
-static GLuint GL_GenAndBind(GLenum target) {
- GLuint id;
- _glGenBuffers(1, &id);
- _glBindBuffer(target, id);
- return id;
-}
-
-GfxResourceID Gfx_CreateIb(void* indices, int indicesCount) {
- GLuint id = GL_GenAndBind(_GL_ELEMENT_ARRAY_BUFFER);
- cc_uint32 size = indicesCount * 2;
- _glBufferData(_GL_ELEMENT_ARRAY_BUFFER, size, indices, _GL_STATIC_DRAW);
- return id;
-}
-
-void Gfx_BindIb(GfxResourceID ib) { _glBindBuffer(_GL_ELEMENT_ARRAY_BUFFER, (GLuint)ib); }
-
-void Gfx_DeleteIb(GfxResourceID* ib) {
- GLuint id = (GLuint)(*ib);
- if (!id) return;
- _glDeleteBuffers(1, &id);
- *ib = 0;
-}
-#else
-GfxResourceID Gfx_CreateIb(void* indices, int indicesCount) { return 0; }
-void Gfx_BindIb(GfxResourceID ib) { }
-void Gfx_DeleteIb(GfxResourceID* ib) { }
-#endif
-
-
-/*########################################################################################################################*
-*------------------------------------------------------Vertex buffers-----------------------------------------------------*
-*#########################################################################################################################*/
-#ifndef CC_BUILD_GL11
-GfxResourceID Gfx_CreateVb(VertexFormat fmt, int count) {
- return GL_GenAndBind(_GL_ARRAY_BUFFER);
-}
-
-void Gfx_BindVb(GfxResourceID vb) { _glBindBuffer(_GL_ARRAY_BUFFER, (GLuint)vb); }
-
-void Gfx_DeleteVb(GfxResourceID* vb) {
- GLuint id = (GLuint)(*vb);
- if (!id) return;
- _glDeleteBuffers(1, &id);
- *vb = 0;
-}
-
-void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) {
- return FastAllocTempMem(count * strideSizes[fmt]);
-}
-
-void Gfx_UnlockVb(GfxResourceID vb) {
- _glBufferData(_GL_ARRAY_BUFFER, tmpSize, tmpData, _GL_STATIC_DRAW);
-}
-#else
-static void UpdateDisplayList(GLuint list, void* vertices, VertexFormat fmt, int count) {
- /* We need to restore client state afer building the list */
- int realFormat = gfx_format;
- void* dyn_data = dynamicListData;
- Gfx_SetVertexFormat(fmt);
- dynamicListData = vertices;
-
- glNewList(list, GL_COMPILE);
- gfx_setupVBFunc();
- glDrawElements(GL_TRIANGLES, ICOUNT(count), GL_UNSIGNED_SHORT, gl_indices);
- glEndList();
-
- Gfx_SetVertexFormat(realFormat);
- dynamicListData = dyn_data;
-}
-
-GfxResourceID Gfx_CreateVb(VertexFormat fmt, int count) { return glGenLists(1); }
-void Gfx_BindVb(GfxResourceID vb) { activeList = (GLuint)vb; }
-
-void Gfx_DeleteVb(GfxResourceID* vb) {
- GLuint id = (GLuint)(*vb);
- if (id) glDeleteLists(id, 1);
- *vb = 0;
-}
-
-/* NOTE! Building chunk in Builder.c relies on vb being ignored */
-/* If that changes, you must fix Builder.c to properly call Gfx_LockVb */
-static VertexFormat tmpFormat;
-static int tmpCount;
-void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) {
- tmpFormat = fmt;
- tmpCount = count;
- return FastAllocTempMem(count * strideSizes[fmt]);
-}
-
-void Gfx_UnlockVb(GfxResourceID vb) {
- UpdateDisplayList((GLuint)vb, tmpData, tmpFormat, tmpCount);
-}
-
-GfxResourceID Gfx_CreateVb2(void* vertices, VertexFormat fmt, int count) {
- GLuint list = glGenLists(1);
- UpdateDisplayList(list, vertices, fmt, count);
- return list;
-}
-#endif
-
-
-/*########################################################################################################################*
-*--------------------------------------------------Dynamic vertex buffers-------------------------------------------------*
-*#########################################################################################################################*/
-#ifndef CC_BUILD_GL11
-GfxResourceID Gfx_CreateDynamicVb(VertexFormat fmt, int maxVertices) {
- GLuint id;
- cc_uint32 size;
- if (Gfx.LostContext) return 0;
-
- id = GL_GenAndBind(_GL_ARRAY_BUFFER);
- size = maxVertices * strideSizes[fmt];
- _glBufferData(_GL_ARRAY_BUFFER, size, NULL, _GL_DYNAMIC_DRAW);
- return id;
-}
-
-void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) {
- return FastAllocTempMem(count * strideSizes[fmt]);
-}
-
-void Gfx_UnlockDynamicVb(GfxResourceID vb) {
- _glBindBuffer(_GL_ARRAY_BUFFER, (GLuint)vb);
- _glBufferSubData(_GL_ARRAY_BUFFER, 0, tmpSize, tmpData);
-}
-
-void Gfx_SetDynamicVbData(GfxResourceID vb, void* vertices, int vCount) {
- cc_uint32 size = vCount * gfx_stride;
- _glBindBuffer(_GL_ARRAY_BUFFER, (GLuint)vb);
- _glBufferSubData(_GL_ARRAY_BUFFER, 0, size, vertices);
-}
-#else
-GfxResourceID Gfx_CreateDynamicVb(VertexFormat fmt, int maxVertices) {
- return (GfxResourceID)Mem_Alloc(maxVertices, strideSizes[fmt], "creating dynamic vb");
-}
-
-void Gfx_BindDynamicVb(GfxResourceID vb) {
- activeList = gl_DYNAMICLISTID;
- dynamicListData = (void*)vb;
-}
-
-void Gfx_DeleteDynamicVb(GfxResourceID* vb) {
- void* addr = (void*)(*vb);
- if (addr) Mem_Free(addr);
- *vb = 0;
-}
-
-void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) { return (void*)vb; }
-void Gfx_UnlockDynamicVb(GfxResourceID vb) { Gfx_BindDynamicVb(vb); }
-
-void Gfx_SetDynamicVbData(GfxResourceID vb, void* vertices, int vCount) {
- Gfx_BindDynamicVb(vb);
- Mem_Copy((void*)vb, vertices, vCount * gfx_stride);
-}
-#endif
-
-
-/*########################################################################################################################*
-*-----------------------------------------------------------Misc----------------------------------------------------------*
-*#########################################################################################################################*/
-static BitmapCol* GL_GetRow(struct Bitmap* bmp, int y) {
- /* OpenGL stores bitmap in bottom-up order, so flip order when saving */
- return Bitmap_GetRow(bmp, (bmp->height - 1) - y);
-}
-cc_result Gfx_TakeScreenshot(struct Stream* output) {
- struct Bitmap bmp;
- cc_result res;
- GLint vp[4];
-
- glGetIntegerv(GL_VIEWPORT, vp); /* { x, y, width, height } */
- bmp.width = vp[2];
- bmp.height = vp[3];
-
- bmp.scan0 = (BitmapCol*)Mem_TryAlloc(bmp.width * bmp.height, 4);
- if (!bmp.scan0) return ERR_OUT_OF_MEMORY;
- glReadPixels(0, 0, bmp.width, bmp.height, PIXEL_FORMAT, TRANSFER_FORMAT, bmp.scan0);
-
- res = Png_Encode(&bmp, output, GL_GetRow, false);
- Mem_Free(bmp.scan0);
- return res;
-}
-
-static void AppendVRAMStats(cc_string* info) {
- static const cc_string memExt = String_FromConst("GL_NVX_gpu_memory_info");
- GLint totalKb, curKb;
- float total, cur;
-
- /* NOTE: glGetString returns UTF8, but I just treat it as code page 437 */
- cc_string exts = String_FromReadonly((const char*)glGetString(GL_EXTENSIONS));
- if (!String_CaselessContains(&exts, &memExt)) return;
-
- glGetIntegerv(0x9048, &totalKb);
- glGetIntegerv(0x9049, &curKb);
- if (totalKb <= 0 || curKb <= 0) return;
-
- total = totalKb / 1024.0f; cur = curKb / 1024.0f;
- String_Format2(info, "Video memory: %f2 MB total, %f2 free\n", &total, &cur);
-}
-
-void Gfx_GetApiInfo(cc_string* info) {
- GLint depthBits;
- int pointerSize = sizeof(void*) * 8;
-
- glGetIntegerv(GL_DEPTH_BITS, &depthBits);
- String_Format1(info, "-- Using OpenGL (%i bit) --\n", &pointerSize);
- String_Format1(info, "Vendor: %c\n", glGetString(GL_VENDOR));
- String_Format1(info, "Renderer: %c\n", glGetString(GL_RENDERER));
- String_Format1(info, "GL version: %c\n", glGetString(GL_VERSION));
- AppendVRAMStats(info);
- String_Format2(info, "Max texture size: (%i, %i)\n", &Gfx.MaxTexWidth, &Gfx.MaxTexHeight);
- String_Format1(info, "Depth buffer bits: %i\n", &depthBits);
- GLContext_GetApiInfo(info);
-}
-
-void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) {
- gfx_minFrameMs = minFrameMs;
- gfx_vsync = vsync;
- if (Gfx.Created) GL_UpdateVsync();
-}
-
-void Gfx_BeginFrame(void) { frameStart = Stopwatch_Measure(); }
-void Gfx_Clear(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); }
-void Gfx_EndFrame(void) {
- if (!GLContext_SwapBuffers()) Gfx_LoseContext("GLContext lost");
- if (gfx_minFrameMs) LimitFPS();
-}
-
-void Gfx_OnWindowResize(void) {
- GLContext_Update();
- /* In case GLContext_Update changes window bounds */
- /* TODO: Eliminate this nasty hack.. */
- Game_UpdateDimensions();
- glViewport(0, 0, Game.Width, Game.Height);
-}
-
-/*########################################################################################################################*
-*------------------------------------------------------OpenGL modern------------------------------------------------------*
-*#########################################################################################################################*/
-#ifdef CC_BUILD_GLMODERN
-#define FTR_TEXTURE_UV (1 << 0)
-#define FTR_ALPHA_TEST (1 << 1)
-#define FTR_TEX_OFFSET (1 << 2)
-#define FTR_LINEAR_FOG (1 << 3)
-#define FTR_DENSIT_FOG (1 << 4)
-#define FTR_HASANY_FOG (FTR_LINEAR_FOG | FTR_DENSIT_FOG)
-#define FTR_FS_MEDIUMP (1 << 7)
-
-#define UNI_MVP_MATRIX (1 << 0)
-#define UNI_TEX_OFFSET (1 << 1)
-#define UNI_FOG_COL (1 << 2)
-#define UNI_FOG_END (1 << 3)
-#define UNI_FOG_DENS (1 << 4)
-#define UNI_MASK_ALL 0x1F
-
-/* cached uniforms (cached for multiple programs */
-static struct Matrix _view, _proj, _mvp;
-static cc_bool gfx_alphaTest, gfx_texTransform;
-static float _texX, _texY;
-
-/* shader programs (emulate fixed function) */
-static struct GLShader {
- int features; /* what features are enabled for this shader */
- int uniforms; /* which associated uniforms need to be resent to GPU */
- GLuint program; /* OpenGL program ID (0 if not yet compiled) */
- int locations[5]; /* location of uniforms (not constant) */
-} shaders[6 * 3] = {
- /* no fog */
- { 0 },
- { 0 | FTR_ALPHA_TEST },
- { FTR_TEXTURE_UV },
- { FTR_TEXTURE_UV | FTR_ALPHA_TEST },
- { FTR_TEXTURE_UV | FTR_TEX_OFFSET },
- { FTR_TEXTURE_UV | FTR_TEX_OFFSET | FTR_ALPHA_TEST },
- /* linear fog */
- { FTR_LINEAR_FOG | 0 },
- { FTR_LINEAR_FOG | 0 | FTR_ALPHA_TEST },
- { FTR_LINEAR_FOG | FTR_TEXTURE_UV },
- { FTR_LINEAR_FOG | FTR_TEXTURE_UV | FTR_ALPHA_TEST },
- { FTR_LINEAR_FOG | FTR_TEXTURE_UV | FTR_TEX_OFFSET },
- { FTR_LINEAR_FOG | FTR_TEXTURE_UV | FTR_TEX_OFFSET | FTR_ALPHA_TEST },
- /* density fog */
- { FTR_DENSIT_FOG | 0 },
- { FTR_DENSIT_FOG | 0 | FTR_ALPHA_TEST },
- { FTR_DENSIT_FOG | FTR_TEXTURE_UV },
- { FTR_DENSIT_FOG | FTR_TEXTURE_UV | FTR_ALPHA_TEST },
- { FTR_DENSIT_FOG | FTR_TEXTURE_UV | FTR_TEX_OFFSET },
- { FTR_DENSIT_FOG | FTR_TEXTURE_UV | FTR_TEX_OFFSET | FTR_ALPHA_TEST },
-};
-static struct GLShader* gfx_activeShader;
-
-/* Generates source code for a GLSL vertex shader, based on shader's flags */
-static void GenVertexShader(const struct GLShader* shader, cc_string* dst) {
- int uv = shader->features & FTR_TEXTURE_UV;
- int tm = shader->features & FTR_TEX_OFFSET;
-
- String_AppendConst(dst, "attribute vec3 in_pos;\n");
- String_AppendConst(dst, "attribute vec4 in_col;\n");
- if (uv) String_AppendConst(dst, "attribute vec2 in_uv;\n");
- String_AppendConst(dst, "varying vec4 out_col;\n");
- if (uv) String_AppendConst(dst, "varying vec2 out_uv;\n");
- String_AppendConst(dst, "uniform mat4 mvp;\n");
- if (tm) String_AppendConst(dst, "uniform vec2 texOffset;\n");
-
- String_AppendConst(dst, "void main() {\n");
- String_AppendConst(dst, " gl_Position = mvp * vec4(in_pos, 1.0);\n");
- String_AppendConst(dst, " out_col = in_col;\n");
- if (uv) String_AppendConst(dst, " out_uv = in_uv;\n");
- if (tm) String_AppendConst(dst, " out_uv = out_uv + texOffset;\n");
- String_AppendConst(dst, "}");
-}
-
-/* Generates source code for a GLSL fragment shader, based on shader's flags */
-static void GenFragmentShader(const struct GLShader* shader, cc_string* dst) {
- int uv = shader->features & FTR_TEXTURE_UV;
- int al = shader->features & FTR_ALPHA_TEST;
- int fl = shader->features & FTR_LINEAR_FOG;
- int fd = shader->features & FTR_DENSIT_FOG;
- int fm = shader->features & FTR_HASANY_FOG;
-
-#ifdef CC_BUILD_GLES
- int mp = shader->features & FTR_FS_MEDIUMP;
- if (mp) String_AppendConst(dst, "precision mediump float;\n");
- else String_AppendConst(dst, "precision highp float;\n");
-#endif
-
- String_AppendConst(dst, "varying vec4 out_col;\n");
- if (uv) String_AppendConst(dst, "varying vec2 out_uv;\n");
- if (uv) String_AppendConst(dst, "uniform sampler2D texImage;\n");
- if (fm) String_AppendConst(dst, "uniform vec3 fogCol;\n");
- if (fl) String_AppendConst(dst, "uniform float fogEnd;\n");
- if (fd) String_AppendConst(dst, "uniform float fogDensity;\n");
-
- String_AppendConst(dst, "void main() {\n");
- if (uv) String_AppendConst(dst, " vec4 col = texture2D(texImage, out_uv) * out_col;\n");
- else String_AppendConst(dst, " vec4 col = out_col;\n");
- if (al) String_AppendConst(dst, " if (col.a < 0.5) discard;\n");
- if (fm) String_AppendConst(dst, " float depth = gl_FragCoord.z / gl_FragCoord.w;\n");
- if (fl) String_AppendConst(dst, " float f = clamp((fogEnd - depth) / fogEnd, 0.0, 1.0);\n");
- if (fd) String_AppendConst(dst, " float f = clamp(exp(fogDensity * depth), 0.0, 1.0);\n");
- if (fm) String_AppendConst(dst, " col.rgb = mix(fogCol, col.rgb, f);\n");
- String_AppendConst(dst, " gl_FragColor = col;\n");
- String_AppendConst(dst, "}");
-}
-
-/* Tries to compile GLSL shader code */
-static GLint CompileShader(GLint shader, const cc_string* src) {
- const char* str = src->buffer;
- int len = src->length;
- GLint temp;
-
- glShaderSource(shader, 1, &str, &len);
- glCompileShader(shader);
- glGetShaderiv(shader, _GL_COMPILE_STATUS, &temp);
- return temp;
-}
-
-/* Logs information then aborts program */
-static void ShaderFailed(GLint shader) {
- char logInfo[2048];
- GLint temp;
- if (!shader) Logger_Abort("Failed to create shader");
-
- temp = 0;
- glGetShaderiv(shader, _GL_INFO_LOG_LENGTH, &temp);
-
- if (temp > 1) {
- glGetShaderInfoLog(shader, 2047, NULL, logInfo);
- logInfo[2047] = '\0';
- Window_ShowDialog("Failed to compile shader", logInfo);
- }
- Logger_Abort("Failed to compile shader");
-}
-
-/* Tries to compile vertex and fragment shaders, then link into an OpenGL program */
-static void CompileProgram(struct GLShader* shader) {
- char tmpBuffer[2048]; cc_string tmp;
- GLuint vs, fs, program;
- GLint temp;
-
- vs = glCreateShader(_GL_VERTEX_SHADER);
- if (!vs) { Platform_LogConst("Failed to create vertex shader"); return; }
-
- String_InitArray(tmp, tmpBuffer);
- GenVertexShader(shader, &tmp);
- if (!CompileShader(vs, &tmp)) ShaderFailed(vs);
-
- fs = glCreateShader(_GL_FRAGMENT_SHADER);
- if (!fs) { Platform_LogConst("Failed to create fragment shader"); glDeleteShader(vs); return; }
-
- tmp.length = 0;
- GenFragmentShader(shader, &tmp);
- if (!CompileShader(fs, &tmp)) {
- /* Sometimes fails 'highp precision is not supported in fragment shader' */
- /* So try compiling shader again without highp precision */
- shader->features |= FTR_FS_MEDIUMP;
-
- tmp.length = 0;
- GenFragmentShader(shader, &tmp);
- if (!CompileShader(fs, &tmp)) ShaderFailed(fs);
- }
-
-
- program = glCreateProgram();
- if (!program) Logger_Abort("Failed to create program");
- shader->program = program;
-
- glAttachShader(program, vs);
- glAttachShader(program, fs);
-
- /* Force in_pos/in_col/in_uv attributes to be bound to 0,1,2 locations */
- /* Although most browsers assign the attributes in this order anyways, */
- /* the specification does not require this. (e.g. Safari doesn't) */
- glBindAttribLocation(program, 0, "in_pos");
- glBindAttribLocation(program, 1, "in_col");
- glBindAttribLocation(program, 2, "in_uv");
-
- glLinkProgram(program);
- glGetProgramiv(program, _GL_LINK_STATUS, &temp);
-
- if (temp) {
- glDetachShader(program, vs);
- glDetachShader(program, fs);
-
- glDeleteShader(vs);
- glDeleteShader(fs);
-
- shader->locations[0] = glGetUniformLocation(program, "mvp");
- shader->locations[1] = glGetUniformLocation(program, "texOffset");
- shader->locations[2] = glGetUniformLocation(program, "fogCol");
- shader->locations[3] = glGetUniformLocation(program, "fogEnd");
- shader->locations[4] = glGetUniformLocation(program, "fogDensity");
- return;
- }
- temp = 0;
- glGetProgramiv(program, _GL_INFO_LOG_LENGTH, &temp);
-
- if (temp > 0) {
- glGetProgramInfoLog(program, 2047, NULL, tmpBuffer);
- tmpBuffer[2047] = '\0';
- Window_ShowDialog("Failed to compile program", tmpBuffer);
- }
- Logger_Abort("Failed to compile program");
-}
-
-/* Marks a uniform as changed on all programs */
-static void DirtyUniform(int uniform) {
- int i;
- for (i = 0; i < Array_Elems(shaders); i++) {
- shaders[i].uniforms |= uniform;
- }
-}
-
-/* Sends changed uniforms to the GPU for current program */
-static void ReloadUniforms(void) {
- struct GLShader* s = gfx_activeShader;
- if (!s) return; /* NULL if context is lost */
-
- if (s->uniforms & UNI_MVP_MATRIX) {
- glUniformMatrix4fv(s->locations[0], 1, false, (float*)&_mvp);
- s->uniforms &= ~UNI_MVP_MATRIX;
- }
- if ((s->uniforms & UNI_TEX_OFFSET) && (s->features & FTR_TEX_OFFSET)) {
- glUniform2f(s->locations[1], _texX, _texY);
- s->uniforms &= ~UNI_TEX_OFFSET;
- }
- if ((s->uniforms & UNI_FOG_COL) && (s->features & FTR_HASANY_FOG)) {
- glUniform3f(s->locations[2], PackedCol_R(gfx_fogColor) / 255.0f, PackedCol_G(gfx_fogColor) / 255.0f,
- PackedCol_B(gfx_fogColor) / 255.0f);
- s->uniforms &= ~UNI_FOG_COL;
- }
- if ((s->uniforms & UNI_FOG_END) && (s->features & FTR_LINEAR_FOG)) {
- glUniform1f(s->locations[3], gfx_fogEnd);
- s->uniforms &= ~UNI_FOG_END;
- }
- if ((s->uniforms & UNI_FOG_DENS) && (s->features & FTR_DENSIT_FOG)) {
- /* See https://docs.microsoft.com/en-us/previous-versions/ms537113(v%3Dvs.85) */
- /* The equation for EXP mode is exp(-density * z), so just negate density here */
- glUniform1f(s->locations[4], -gfx_fogDensity);
- s->uniforms &= ~UNI_FOG_DENS;
- }
-}
-
-/* Switches program to one that duplicates current fixed function state */
-/* Compiles program and reloads uniforms if needed */
-static void SwitchProgram(void) {
- struct GLShader* shader;
- int index = 0;
-
- if (gfx_fogEnabled) {
- index += 6; /* linear fog */
- if (gfx_fogMode >= 1) index += 6; /* exp fog */
- }
-
- if (gfx_format == VERTEX_FORMAT_TEXTURED) index += 2;
- if (gfx_texTransform) index += 2;
- if (gfx_alphaTest) index += 1;
-
- shader = &shaders[index];
- if (shader == gfx_activeShader) { ReloadUniforms(); return; }
- if (!shader->program) CompileProgram(shader);
-
- gfx_activeShader = shader;
- glUseProgram(shader->program);
- ReloadUniforms();
-}
-
-void Gfx_SetFog(cc_bool enabled) { gfx_fogEnabled = enabled; SwitchProgram(); }
-void Gfx_SetFogCol(PackedCol col) {
- if (col == gfx_fogColor) return;
- gfx_fogColor = col;
- DirtyUniform(UNI_FOG_COL);
- ReloadUniforms();
-}
-
-void Gfx_SetFogDensity(float value) {
- if (gfx_fogDensity == value) return;
- gfx_fogDensity = value;
- DirtyUniform(UNI_FOG_DENS);
- ReloadUniforms();
-}
-
-void Gfx_SetFogEnd(float value) {
- if (gfx_fogEnd == value) return;
- gfx_fogEnd = value;
- DirtyUniform(UNI_FOG_END);
- ReloadUniforms();
-}
-
-void Gfx_SetFogMode(FogFunc func) {
- if (gfx_fogMode == func) return;
- gfx_fogMode = func;
- SwitchProgram();
-}
-
-void Gfx_SetTexturing(cc_bool enabled) { }
-void Gfx_SetAlphaTest(cc_bool enabled) { gfx_alphaTest = enabled; SwitchProgram(); }
-
-void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) {
- if (type == MATRIX_VIEW) _view = *matrix;
- if (type == MATRIX_PROJECTION) _proj = *matrix;
-
- Matrix_Mul(&_mvp, &_view, &_proj);
- DirtyUniform(UNI_MVP_MATRIX);
- ReloadUniforms();
-}
-void Gfx_LoadIdentityMatrix(MatrixType type) {
- Gfx_LoadMatrix(type, &Matrix_Identity);
-}
-
-void Gfx_EnableTextureOffset(float x, float y) {
- _texX = x; _texY = y;
- gfx_texTransform = true;
- DirtyUniform(UNI_TEX_OFFSET);
- SwitchProgram();
-}
-
-void Gfx_DisableTextureOffset(void) {
- gfx_texTransform = false;
- SwitchProgram();
-}
-
-static void GL_CheckSupport(void) {
-#ifndef CC_BUILD_GLES
- customMipmapsLevels = true;
-#endif
-}
-
-static void Gfx_FreeState(void) {
- int i;
- FreeDefaultResources();
- gfx_activeShader = NULL;
-
- for (i = 0; i < Array_Elems(shaders); i++) {
- glDeleteProgram(shaders[i].program);
- shaders[i].program = 0;
- }
-}
-
-static void Gfx_RestoreState(void) {
- InitDefaultResources();
- glEnableVertexAttribArray(0);
- glEnableVertexAttribArray(1);
- gfx_format = -1;
-
- DirtyUniform(UNI_MASK_ALL);
- GL_ClearCol(gfx_clearColor);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glDepthFunc(GL_LEQUAL);
-}
-cc_bool Gfx_WarnIfNecessary(void) { return false; }
-
-static void GL_SetupVbColoured(void) {
- glVertexAttribPointer(0, 3, GL_FLOAT, false, SIZEOF_VERTEX_COLOURED, (void*)0);
- glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, SIZEOF_VERTEX_COLOURED, (void*)12);
-}
-
-static void GL_SetupVbTextured(void) {
- glVertexAttribPointer(0, 3, GL_FLOAT, false, SIZEOF_VERTEX_TEXTURED, (void*)0);
- glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, SIZEOF_VERTEX_TEXTURED, (void*)12);
- glVertexAttribPointer(2, 2, GL_FLOAT, false, SIZEOF_VERTEX_TEXTURED, (void*)16);
-}
-
-static void GL_SetupVbColoured_Range(int startVertex) {
- cc_uint32 offset = startVertex * SIZEOF_VERTEX_COLOURED;
- glVertexAttribPointer(0, 3, GL_FLOAT, false, SIZEOF_VERTEX_COLOURED, (void*)(offset));
- glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, SIZEOF_VERTEX_COLOURED, (void*)(offset + 12));
-}
-
-static void GL_SetupVbTextured_Range(int startVertex) {
- cc_uint32 offset = startVertex * SIZEOF_VERTEX_TEXTURED;
- glVertexAttribPointer(0, 3, GL_FLOAT, false, SIZEOF_VERTEX_TEXTURED, (void*)(offset));
- glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, SIZEOF_VERTEX_TEXTURED, (void*)(offset + 12));
- glVertexAttribPointer(2, 2, GL_FLOAT, false, SIZEOF_VERTEX_TEXTURED, (void*)(offset + 16));
-}
-
-void Gfx_SetVertexFormat(VertexFormat fmt) {
- if (fmt == gfx_format) return;
- gfx_format = fmt;
- gfx_stride = strideSizes[fmt];
-
- if (fmt == VERTEX_FORMAT_TEXTURED) {
- glEnableVertexAttribArray(2);
- gfx_setupVBFunc = GL_SetupVbTextured;
- gfx_setupVBRangeFunc = GL_SetupVbTextured_Range;
- } else {
- glDisableVertexAttribArray(2);
- gfx_setupVBFunc = GL_SetupVbColoured;
- gfx_setupVBRangeFunc = GL_SetupVbColoured_Range;
- }
- SwitchProgram();
-}
-
-void Gfx_DrawVb_Lines(int verticesCount) {
- gfx_setupVBFunc();
- glDrawArrays(GL_LINES, 0, verticesCount);
-}
-
-void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) {
- gfx_setupVBRangeFunc(startVertex);
- glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, NULL);
-}
-
-void Gfx_DrawVb_IndexedTris(int verticesCount) {
- gfx_setupVBFunc();
- glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, NULL);
-}
-
-void Gfx_BindVb_Textured(GfxResourceID vb) {
- Gfx_BindVb(vb);
- GL_SetupVbTextured();
-}
-
-void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) {
- if (startVertex + verticesCount > GFX_MAX_VERTICES) {
- GL_SetupVbTextured_Range(startVertex);
- glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, NULL);
- GL_SetupVbTextured();
- } else {
- /* ICOUNT(startVertex) * 2 = startVertex * 3 */
- glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, (void*)(startVertex * 3));
- }
-}
-#endif
-
-
-/*########################################################################################################################*
-*------------------------------------------------------OpenGL legacy------------------------------------------------------*
-*#########################################################################################################################*/
-#ifndef CC_BUILD_GLMODERN
-void Gfx_SetFog(cc_bool enabled) {
- gfx_fogEnabled = enabled;
- gl_Toggle(GL_FOG);
-}
-
-void Gfx_SetFogCol(PackedCol col) {
- float rgba[4];
- if (col == gfx_fogColor) return;
-
- rgba[0] = PackedCol_R(col) / 255.0f; rgba[1] = PackedCol_G(col) / 255.0f;
- rgba[2] = PackedCol_B(col) / 255.0f; rgba[3] = PackedCol_A(col) / 255.0f;
-
- glFogfv(GL_FOG_COLOR, rgba);
- gfx_fogColor = col;
-}
-
-void Gfx_SetFogDensity(float value) {
- if (value == gfx_fogDensity) return;
- glFogf(GL_FOG_DENSITY, value);
- gfx_fogDensity = value;
-}
-
-void Gfx_SetFogEnd(float value) {
- if (value == gfx_fogEnd) return;
- glFogf(GL_FOG_END, value);
- gfx_fogEnd = value;
-}
-
-void Gfx_SetFogMode(FogFunc func) {
- static GLint modes[3] = { GL_LINEAR, GL_EXP, GL_EXP2 };
- if (func == gfx_fogMode) return;
-
-#ifdef CC_BUILD_GLES
- /* OpenGL ES doesn't support glFogi, so use glFogf instead */
- /* https://www.khronos.org/registry/OpenGL-Refpages/es1.1/xhtml/ */
- glFogf(GL_FOG_MODE, modes[func]);
-#else
- glFogi(GL_FOG_MODE, modes[func]);
-#endif
- gfx_fogMode = func;
-}
-
-void Gfx_SetTexturing(cc_bool enabled) { gl_Toggle(GL_TEXTURE_2D); }
-void Gfx_SetAlphaTest(cc_bool enabled) { gl_Toggle(GL_ALPHA_TEST); }
-
-static GLenum matrix_modes[3] = { GL_PROJECTION, GL_MODELVIEW, GL_TEXTURE };
-static int lastMatrix;
-
-void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) {
- if (type != lastMatrix) { lastMatrix = type; glMatrixMode(matrix_modes[type]); }
- glLoadMatrixf((const float*)matrix);
-}
-
-void Gfx_LoadIdentityMatrix(MatrixType type) {
- if (type != lastMatrix) { lastMatrix = type; glMatrixMode(matrix_modes[type]); }
- glLoadIdentity();
-}
-
-static struct Matrix texMatrix = Matrix_IdentityValue;
-void Gfx_EnableTextureOffset(float x, float y) {
- texMatrix.row4.X = x; texMatrix.row4.Y = y;
- Gfx_LoadMatrix(2, &texMatrix);
-}
-
-void Gfx_DisableTextureOffset(void) { Gfx_LoadIdentityMatrix(2); }
-
-static void Gfx_FreeState(void) { FreeDefaultResources(); }
-static void Gfx_RestoreState(void) {
- InitDefaultResources();
- glEnableClientState(GL_VERTEX_ARRAY);
- glEnableClientState(GL_COLOR_ARRAY);
- gfx_format = -1;
-
- glHint(GL_FOG_HINT, GL_NICEST);
- glAlphaFunc(GL_GREATER, 0.5f);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glDepthFunc(GL_LEQUAL);
-}
-
-cc_bool Gfx_WarnIfNecessary(void) {
- cc_string renderer = String_FromReadonly((const char*)glGetString(GL_RENDERER));
-
-#ifdef CC_BUILD_GL11
- Chat_AddRaw("&cYou are using the very outdated OpenGL backend.");
- Chat_AddRaw("&cAs such you may experience poor performance.");
- Chat_AddRaw("&cIt is likely you need to install video card drivers.");
-#endif
- if (!String_ContainsConst(&renderer, "Intel")) return false;
-
- Chat_AddRaw("&cIntel graphics cards are known to have issues with the OpenGL build.");
- Chat_AddRaw("&cVSync may not work, and you may see disappearing clouds and map edges.");
-#ifdef CC_BUILD_WIN
- Chat_AddRaw("&cTry downloading the Direct3D 9 build instead.");
-#endif
- return true;
-}
-
-
-/*########################################################################################################################*
-*-------------------------------------------------------Compatibility-----------------------------------------------------*
-*#########################################################################################################################*/
-#ifdef CC_BUILD_GL11
-static void GL_CheckSupport(void) { MakeIndices(gl_indices, GFX_MAX_INDICES); }
-#else
-/* fake vertex buffer objects with client side pointers */
-typedef struct fake_buffer { cc_uint8* data; } fake_buffer;
-static fake_buffer* cur_ib;
-static fake_buffer* cur_vb;
-#define fake_GetBuffer(target) (target == _GL_ELEMENT_ARRAY_BUFFER ? &cur_ib : &cur_vb);
-
-static void APIENTRY fake_glBindBuffer(GLenum target, GLuint src) {
- fake_buffer** buffer = fake_GetBuffer(target);
- *buffer = (fake_buffer*)src;
-}
-
-static void APIENTRY fake_glDeleteBuffers(GLsizei n, const GLuint *buffers) {
- Mem_Free((void*)buffers[0]);
-}
-
-static void APIENTRY fake_glGenBuffers(GLsizei n, GLuint *buffers) {
- fake_buffer* buffer = (fake_buffer*)Mem_TryAlloc(1, sizeof(fake_buffer));
- buffer->data = NULL;
- buffers[0] = (GLuint)buffer;
-}
-
-static void APIENTRY fake_glBufferData(GLenum target, cc_uintptr size, const GLvoid* data, GLenum usage) {
- fake_buffer* buffer = *fake_GetBuffer(target);
- Mem_Free(buffer->data);
-
- buffer->data = Mem_TryAlloc(size, 1);
- if (data) Mem_Copy(buffer->data, data, size);
-}
-static void APIENTRY fake_glBufferSubData(GLenum target, cc_uintptr offset, cc_uintptr size, const GLvoid* data) {
- fake_buffer* buffer = *fake_GetBuffer(target);
- Mem_Copy(buffer->data, data, size);
-}
-
-static void GL_CheckSupport(void) {
- static const struct DynamicLibSym coreVboFuncs[5] = {
- DynamicLib_Sym2("glBindBuffer", glBindBuffer), DynamicLib_Sym2("glDeleteBuffers", glDeleteBuffers),
- DynamicLib_Sym2("glGenBuffers", glGenBuffers), DynamicLib_Sym2("glBufferData", glBufferData),
- DynamicLib_Sym2("glBufferSubData", glBufferSubData)
- };
- static const struct DynamicLibSym arbVboFuncs[5] = {
- DynamicLib_Sym2("glBindBufferARB", glBindBuffer), DynamicLib_Sym2("glDeleteBuffersARB", glDeleteBuffers),
- DynamicLib_Sym2("glGenBuffersARB", glGenBuffers), DynamicLib_Sym2("glBufferDataARB", glBufferData),
- DynamicLib_Sym2("glBufferSubDataARB", glBufferSubData)
- };
- static const cc_string vboExt = String_FromConst("GL_ARB_vertex_buffer_object");
- cc_string extensions = String_FromReadonly((const char*)glGetString(GL_EXTENSIONS));
- const GLubyte* ver = glGetString(GL_VERSION);
-
- /* Version string is always: x.y. (and whatever afterwards) */
- int major = ver[0] - '0', minor = ver[2] - '0';
-
- /* Supported in core since 1.5 */
- if (major > 1 || (major == 1 && minor >= 5)) {
- GLContext_GetAll(coreVboFuncs, Array_Elems(coreVboFuncs));
- } else if (String_CaselessContains(&extensions, &vboExt)) {
- GLContext_GetAll(arbVboFuncs, Array_Elems(arbVboFuncs));
- } else {
- Logger_Abort("Only OpenGL 1.1 supported.\n\n" \
- "Compile the game with CC_BUILD_GL11, or ask on the ClassiCube forums for it");
-
- _glBindBuffer = fake_glBindBuffer; _glDeleteBuffers = fake_glDeleteBuffers;
- _glGenBuffers = fake_glGenBuffers; _glBufferData = fake_glBufferData;
- _glBufferSubData = fake_glBufferSubData;
- }
- customMipmapsLevels = true;
-}
-#endif
-
-
-/*########################################################################################################################*
-*----------------------------------------------------------Drawing--------------------------------------------------------*
-*#########################################################################################################################*/
-#ifdef CC_BUILD_GL11
-/* point to client side dynamic array */
-#define VB_PTR ((cc_uint8*)dynamicListData)
-#define IB_PTR gl_indices
-#else
-/* no client side array, use vertex buffer object */
-#define VB_PTR 0
-#define IB_PTR NULL
-#endif
-
-static void GL_SetupVbColoured(void) {
- glVertexPointer(3, GL_FLOAT, SIZEOF_VERTEX_COLOURED, (void*)(VB_PTR + 0));
- glColorPointer(4, GL_UNSIGNED_BYTE, SIZEOF_VERTEX_COLOURED, (void*)(VB_PTR + 12));
-}
-
-static void GL_SetupVbTextured(void) {
- glVertexPointer(3, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + 0));
- glColorPointer(4, GL_UNSIGNED_BYTE, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + 12));
- glTexCoordPointer(2, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + 16));
-}
-
-static void GL_SetupVbColoured_Range(int startVertex) {
- cc_uint32 offset = startVertex * SIZEOF_VERTEX_COLOURED;
- glVertexPointer(3, GL_FLOAT, SIZEOF_VERTEX_COLOURED, (void*)(VB_PTR + offset));
- glColorPointer(4, GL_UNSIGNED_BYTE, SIZEOF_VERTEX_COLOURED, (void*)(VB_PTR + offset + 12));
-}
-
-static void GL_SetupVbTextured_Range(int startVertex) {
- cc_uint32 offset = startVertex * SIZEOF_VERTEX_TEXTURED;
- glVertexPointer(3, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset));
- glColorPointer(4, GL_UNSIGNED_BYTE, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset + 12));
- glTexCoordPointer(2, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset + 16));
-}
-
-void Gfx_SetVertexFormat(VertexFormat fmt) {
- if (fmt == gfx_format) return;
- gfx_format = fmt;
- gfx_stride = strideSizes[fmt];
-
- if (fmt == VERTEX_FORMAT_TEXTURED) {
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- gfx_setupVBFunc = GL_SetupVbTextured;
- gfx_setupVBRangeFunc = GL_SetupVbTextured_Range;
- } else {
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- gfx_setupVBFunc = GL_SetupVbColoured;
- gfx_setupVBRangeFunc = GL_SetupVbColoured_Range;
- }
-}
-
-void Gfx_DrawVb_Lines(int verticesCount) {
- gfx_setupVBFunc();
- glDrawArrays(GL_LINES, 0, verticesCount);
-}
-
-void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) {
-#ifdef CC_BUILD_GL11
- if (activeList != gl_DYNAMICLISTID) { glCallList(activeList); return; }
-#endif
- gfx_setupVBRangeFunc(startVertex);
- glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, IB_PTR);
-}
-
-void Gfx_DrawVb_IndexedTris(int verticesCount) {
-#ifdef CC_BUILD_GL11
- if (activeList != gl_DYNAMICLISTID) { glCallList(activeList); return; }
-#endif
- gfx_setupVBFunc();
- glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, IB_PTR);
-}
-
-#ifdef CC_BUILD_GL11
-void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { glCallList(activeList); }
-#else
-void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) {
- cc_uint32 offset = startVertex * SIZEOF_VERTEX_TEXTURED;
- glVertexPointer(3, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset));
- glColorPointer(4, GL_UNSIGNED_BYTE, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset + 12));
- glTexCoordPointer(2, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset + 16));
- glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, IB_PTR);
-}
-#endif /* !CC_BUILD_GL11 */
-#endif /* !CC_BUILD_GLMODERN */
-#endif
diff --git a/src/Graphics_GL1.c b/src/Graphics_GL1.c
new file mode 100644
index 000000000..fe09280df
--- /dev/null
+++ b/src/Graphics_GL1.c
@@ -0,0 +1,593 @@
+#include "Core.h"
+#if defined CC_BUILD_GL && !defined CC_BUILD_GLMODERN
+#include "_GraphicsBase.h"
+#include "Chat.h"
+#include "Errors.h"
+#include "Logger.h"
+#include "Window.h"
+/* The OpenGL backend is a bit of a mess, since it's really 2 backends in one:
+ * - OpenGL 1.1 (completely lacking GPU, fallbacks to say Windows built-in software rasteriser)
+ * - OpenGL 1.5 or OpenGL 1.2 + GL_ARB_vertex_buffer_object (default desktop backend)
+*/
+
+#if defined CC_BUILD_WIN
+/* Avoid pointless includes */
+#define WIN32_LEAN_AND_MEAN
+#define NOSERVICE
+#define NOMCX
+#define NOIME
+#include
+#define GLAPI WINGDIAPI
+#else
+#define GLAPI extern
+#define APIENTRY
+#endif
+
+/* === BEGIN OPENGL HEADERS === */
+typedef unsigned int GLenum;
+typedef unsigned char GLboolean;
+typedef signed char GLbyte;
+typedef int GLint;
+typedef int GLsizei;
+typedef unsigned char GLubyte;
+typedef unsigned int GLuint;
+typedef float GLfloat;
+typedef void GLvoid;
+
+#define GL_LEQUAL 0x0203
+#define GL_GREATER 0x0204
+
+#define GL_DEPTH_BUFFER_BIT 0x00000100
+#define GL_COLOR_BUFFER_BIT 0x00004000
+
+#define GL_LINES 0x0001
+#define GL_TRIANGLES 0x0004
+
+#define GL_BLEND 0x0BE2
+#define GL_SRC_ALPHA 0x0302
+#define GL_ONE_MINUS_SRC_ALPHA 0x0303
+
+#define GL_UNSIGNED_BYTE 0x1401
+#define GL_UNSIGNED_SHORT 0x1403
+#define GL_UNSIGNED_INT 0x1405
+#define GL_FLOAT 0x1406
+#define GL_RGBA 0x1908
+#define GL_BGRA_EXT 0x80E1
+#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+
+#define GL_FOG 0x0B60
+#define GL_FOG_DENSITY 0x0B62
+#define GL_FOG_END 0x0B64
+#define GL_FOG_MODE 0x0B65
+#define GL_FOG_COLOR 0x0B66
+#define GL_LINEAR 0x2601
+#define GL_EXP 0x0800
+#define GL_EXP2 0x0801
+
+#define GL_CULL_FACE 0x0B44
+#define GL_DEPTH_TEST 0x0B71
+#define GL_MATRIX_MODE 0x0BA0
+#define GL_VIEWPORT 0x0BA2
+#define GL_ALPHA_TEST 0x0BC0
+#define GL_MAX_TEXTURE_SIZE 0x0D33
+#define GL_DEPTH_BITS 0x0D56
+
+#define GL_FOG_HINT 0x0C54
+#define GL_NICEST 0x1102
+#define GL_COMPILE 0x1300
+
+#define GL_MODELVIEW 0x1700
+#define GL_PROJECTION 0x1701
+#define GL_TEXTURE 0x1702
+
+#define GL_VENDOR 0x1F00
+#define GL_RENDERER 0x1F01
+#define GL_VERSION 0x1F02
+#define GL_EXTENSIONS 0x1F03
+
+#define GL_TEXTURE_2D 0x0DE1
+#define GL_NEAREST 0x2600
+#define GL_NEAREST_MIPMAP_LINEAR 0x2702
+#define GL_TEXTURE_MAG_FILTER 0x2800
+#define GL_TEXTURE_MIN_FILTER 0x2801
+
+#define GL_VERTEX_ARRAY 0x8074
+#define GL_COLOR_ARRAY 0x8076
+#define GL_TEXTURE_COORD_ARRAY 0x8078
+
+/* Not present in gl.h on Windows (only up to OpenGL 1.1) */
+#define GL_ARRAY_BUFFER 0x8892
+#define GL_ELEMENT_ARRAY_BUFFER 0x8893
+#define GL_STATIC_DRAW 0x88E4
+#define GL_DYNAMIC_DRAW 0x88E8
+
+GLAPI void APIENTRY glAlphaFunc(GLenum func, GLfloat ref);
+GLAPI void APIENTRY glBindTexture(GLenum target, GLuint texture);
+GLAPI void APIENTRY glBlendFunc(GLenum sfactor, GLenum dfactor);
+GLAPI void APIENTRY glCallList(GLuint list);
+GLAPI void APIENTRY glClear(GLuint mask);
+GLAPI void APIENTRY glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
+GLAPI void APIENTRY glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
+GLAPI void APIENTRY glColorPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer);
+GLAPI void APIENTRY glDeleteLists(GLuint list, GLsizei range);
+GLAPI void APIENTRY glDeleteTextures(GLsizei n, const GLuint* textures);
+GLAPI void APIENTRY glDepthFunc(GLenum func);
+GLAPI void APIENTRY glDepthMask(GLboolean flag);
+GLAPI void APIENTRY glDisable(GLenum cap);
+GLAPI void APIENTRY glDisableClientState(GLenum array);
+GLAPI void APIENTRY glDrawArrays(GLenum mode, GLint first, GLsizei count);
+GLAPI void APIENTRY glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices);
+GLAPI void APIENTRY glEnable(GLenum cap);
+GLAPI void APIENTRY glEnableClientState(GLenum array);
+GLAPI void APIENTRY glEndList(void);
+GLAPI void APIENTRY glFogf(GLenum pname, GLfloat param);
+GLAPI void APIENTRY glFogfv(GLenum pname, const GLfloat* params);
+GLAPI void APIENTRY glFogi(GLenum pname, GLint param);
+GLAPI void APIENTRY glFogiv(GLenum pname, const GLint* params);
+GLAPI GLuint APIENTRY glGenLists(GLsizei range);
+GLAPI void APIENTRY glGenTextures(GLsizei n, GLuint* textures);
+GLAPI GLenum APIENTRY glGetError(void);
+GLAPI void APIENTRY glGetFloatv(GLenum pname, GLfloat* params);
+GLAPI void APIENTRY glGetIntegerv(GLenum pname, GLint* params);
+GLAPI const GLubyte* APIENTRY glGetString(GLenum name);
+GLAPI void APIENTRY glHint(GLenum target, GLenum mode);
+GLAPI void APIENTRY glLoadIdentity(void);
+GLAPI void APIENTRY glLoadMatrixf(const GLfloat* m);
+GLAPI void APIENTRY glMatrixMode(GLenum mode);
+GLAPI void APIENTRY glNewList(GLuint list, GLenum mode);
+GLAPI void APIENTRY glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels);
+GLAPI void APIENTRY glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer);
+GLAPI void APIENTRY glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels);
+GLAPI void APIENTRY glTexParameteri(GLenum target, GLenum pname, GLint param);
+GLAPI void APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels);
+GLAPI void APIENTRY glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer);
+GLAPI void APIENTRY glViewport(GLint x, GLint y, GLsizei width, GLsizei height);
+/* === END OPENGL HEADERS === */
+
+
+#if defined CC_BUILD_GL11
+static GLuint activeList;
+#define gl_DYNAMICLISTID 1234567891
+static void* dynamicListData;
+static cc_uint16 gl_indices[GFX_MAX_INDICES];
+#else
+/* OpenGL functions use stdcall instead of cdecl on Windows */
+static void (APIENTRY *_glBindBuffer)(GLenum target, GLuint buffer);
+static void (APIENTRY *_glDeleteBuffers)(GLsizei n, const GLuint *buffers);
+static void (APIENTRY *_glGenBuffers)(GLsizei n, GLuint *buffers);
+static void (APIENTRY *_glBufferData)(GLenum target, cc_uintptr size, const GLvoid* data, GLenum usage);
+static void (APIENTRY *_glBufferSubData)(GLenum target, cc_uintptr offset, cc_uintptr size, const GLvoid* data);
+#endif
+#include "_GLShared.h"
+
+typedef void (*GL_SetupVBFunc)(void);
+typedef void (*GL_SetupVBRangeFunc)(int startVertex);
+static GL_SetupVBFunc gfx_setupVBFunc;
+static GL_SetupVBRangeFunc gfx_setupVBRangeFunc;
+/* Current format and size of vertices */
+static int gfx_stride, gfx_format = -1;
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------Index buffers-----------------------------------------------------*
+*#########################################################################################################################*/
+#ifndef CC_BUILD_GL11
+static GLuint GL_GenAndBind(GLenum target) {
+ GLuint id;
+ _glGenBuffers(1, &id);
+ _glBindBuffer(target, id);
+ return id;
+}
+
+GfxResourceID Gfx_CreateIb(void* indices, int indicesCount) {
+ GLuint id = GL_GenAndBind(GL_ELEMENT_ARRAY_BUFFER);
+ cc_uint32 size = indicesCount * 2;
+ _glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, indices, GL_STATIC_DRAW);
+ return id;
+}
+
+void Gfx_BindIb(GfxResourceID ib) { _glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)ib); }
+
+void Gfx_DeleteIb(GfxResourceID* ib) {
+ GLuint id = (GLuint)(*ib);
+ if (!id) return;
+ _glDeleteBuffers(1, &id);
+ *ib = 0;
+}
+#else
+GfxResourceID Gfx_CreateIb(void* indices, int indicesCount) { return 0; }
+void Gfx_BindIb(GfxResourceID ib) { }
+void Gfx_DeleteIb(GfxResourceID* ib) { }
+#endif
+
+
+/*########################################################################################################################*
+*------------------------------------------------------Vertex buffers-----------------------------------------------------*
+*#########################################################################################################################*/
+#ifndef CC_BUILD_GL11
+GfxResourceID Gfx_CreateVb(VertexFormat fmt, int count) {
+ return GL_GenAndBind(GL_ARRAY_BUFFER);
+}
+
+void Gfx_BindVb(GfxResourceID vb) { _glBindBuffer(GL_ARRAY_BUFFER, (GLuint)vb); }
+
+void Gfx_DeleteVb(GfxResourceID* vb) {
+ GLuint id = (GLuint)(*vb);
+ if (!id) return;
+ _glDeleteBuffers(1, &id);
+ *vb = 0;
+}
+
+void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) {
+ return FastAllocTempMem(count * strideSizes[fmt]);
+}
+
+void Gfx_UnlockVb(GfxResourceID vb) {
+ _glBufferData(GL_ARRAY_BUFFER, tmpSize, tmpData, GL_STATIC_DRAW);
+}
+#else
+static void UpdateDisplayList(GLuint list, void* vertices, VertexFormat fmt, int count);
+GfxResourceID Gfx_CreateVb(VertexFormat fmt, int count) { return glGenLists(1); }
+void Gfx_BindVb(GfxResourceID vb) { activeList = (GLuint)vb; }
+
+void Gfx_DeleteVb(GfxResourceID* vb) {
+ GLuint id = (GLuint)(*vb);
+ if (id) glDeleteLists(id, 1);
+ *vb = 0;
+}
+
+/* NOTE! Building chunk in Builder.c relies on vb being ignored */
+/* If that changes, you must fix Builder.c to properly call Gfx_LockVb */
+static VertexFormat tmpFormat;
+static int tmpCount;
+void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) {
+ tmpFormat = fmt;
+ tmpCount = count;
+ return FastAllocTempMem(count * strideSizes[fmt]);
+}
+
+void Gfx_UnlockVb(GfxResourceID vb) {
+ UpdateDisplayList((GLuint)vb, tmpData, tmpFormat, tmpCount);
+}
+
+GfxResourceID Gfx_CreateVb2(void* vertices, VertexFormat fmt, int count) {
+ GLuint list = glGenLists(1);
+ UpdateDisplayList(list, vertices, fmt, count);
+ return list;
+}
+#endif
+
+
+/*########################################################################################################################*
+*--------------------------------------------------Dynamic vertex buffers-------------------------------------------------*
+*#########################################################################################################################*/
+#ifndef CC_BUILD_GL11
+GfxResourceID Gfx_CreateDynamicVb(VertexFormat fmt, int maxVertices) {
+ GLuint id;
+ cc_uint32 size;
+ if (Gfx.LostContext) return 0;
+
+ id = GL_GenAndBind(GL_ARRAY_BUFFER);
+ size = maxVertices * strideSizes[fmt];
+ _glBufferData(GL_ARRAY_BUFFER, size, NULL, GL_DYNAMIC_DRAW);
+ return id;
+}
+
+void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) {
+ return FastAllocTempMem(count * strideSizes[fmt]);
+}
+
+void Gfx_UnlockDynamicVb(GfxResourceID vb) {
+ _glBindBuffer(GL_ARRAY_BUFFER, (GLuint)vb);
+ _glBufferSubData(GL_ARRAY_BUFFER, 0, tmpSize, tmpData);
+}
+
+void Gfx_SetDynamicVbData(GfxResourceID vb, void* vertices, int vCount) {
+ cc_uint32 size = vCount * gfx_stride;
+ _glBindBuffer(GL_ARRAY_BUFFER, (GLuint)vb);
+ _glBufferSubData(GL_ARRAY_BUFFER, 0, size, vertices);
+}
+#else
+GfxResourceID Gfx_CreateDynamicVb(VertexFormat fmt, int maxVertices) {
+ return (GfxResourceID)Mem_Alloc(maxVertices, strideSizes[fmt], "creating dynamic vb");
+}
+
+void Gfx_BindDynamicVb(GfxResourceID vb) {
+ activeList = gl_DYNAMICLISTID;
+ dynamicListData = (void*)vb;
+}
+
+void Gfx_DeleteDynamicVb(GfxResourceID* vb) {
+ void* addr = (void*)(*vb);
+ if (addr) Mem_Free(addr);
+ *vb = 0;
+}
+
+void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) { return (void*)vb; }
+void Gfx_UnlockDynamicVb(GfxResourceID vb) { Gfx_BindDynamicVb(vb); }
+
+void Gfx_SetDynamicVbData(GfxResourceID vb, void* vertices, int vCount) {
+ Gfx_BindDynamicVb(vb);
+ Mem_Copy((void*)vb, vertices, vCount * gfx_stride);
+}
+#endif
+
+
+/*########################################################################################################################*
+*-----------------------------------------------------State management----------------------------------------------------*
+*#########################################################################################################################*/
+static PackedCol gfx_fogColor;
+static float gfx_fogEnd = -1.0f, gfx_fogDensity = -1.0f;
+static int gfx_fogMode = -1;
+
+void Gfx_SetFog(cc_bool enabled) {
+ gfx_fogEnabled = enabled;
+ if (enabled) { glEnable(GL_FOG); } else { glDisable(GL_FOG); }
+}
+
+void Gfx_SetFogCol(PackedCol col) {
+ float rgba[4];
+ if (col == gfx_fogColor) return;
+
+ rgba[0] = PackedCol_R(col) / 255.0f; rgba[1] = PackedCol_G(col) / 255.0f;
+ rgba[2] = PackedCol_B(col) / 255.0f; rgba[3] = PackedCol_A(col) / 255.0f;
+
+ glFogfv(GL_FOG_COLOR, rgba);
+ gfx_fogColor = col;
+}
+
+void Gfx_SetFogDensity(float value) {
+ if (value == gfx_fogDensity) return;
+ glFogf(GL_FOG_DENSITY, value);
+ gfx_fogDensity = value;
+}
+
+void Gfx_SetFogEnd(float value) {
+ if (value == gfx_fogEnd) return;
+ glFogf(GL_FOG_END, value);
+ gfx_fogEnd = value;
+}
+
+void Gfx_SetFogMode(FogFunc func) {
+ static GLint modes[3] = { GL_LINEAR, GL_EXP, GL_EXP2 };
+ if (func == gfx_fogMode) return;
+
+#ifdef CC_BUILD_GLES
+ /* OpenGL ES doesn't support glFogi, so use glFogf instead */
+ /* https://www.khronos.org/registry/OpenGL-Refpages/es1.1/xhtml/ */
+ glFogf(GL_FOG_MODE, modes[func]);
+#else
+ glFogi(GL_FOG_MODE, modes[func]);
+#endif
+ gfx_fogMode = func;
+}
+
+void Gfx_SetTexturing(cc_bool enabled) {
+ if (enabled) { glEnable(GL_TEXTURE_2D); } else { glDisable(GL_TEXTURE_2D); }
+}
+void Gfx_SetAlphaTest(cc_bool enabled) {
+ if (enabled) { glEnable(GL_ALPHA_TEST); } else { glDisable(GL_ALPHA_TEST); }
+}
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Matrices--------------------------------------------------------*
+*#########################################################################################################################*/
+static GLenum matrix_modes[3] = { GL_PROJECTION, GL_MODELVIEW, GL_TEXTURE };
+static int lastMatrix;
+
+void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) {
+ if (type != lastMatrix) { lastMatrix = type; glMatrixMode(matrix_modes[type]); }
+ glLoadMatrixf((const float*)matrix);
+}
+
+void Gfx_LoadIdentityMatrix(MatrixType type) {
+ if (type != lastMatrix) { lastMatrix = type; glMatrixMode(matrix_modes[type]); }
+ glLoadIdentity();
+}
+
+static struct Matrix texMatrix = Matrix_IdentityValue;
+void Gfx_EnableTextureOffset(float x, float y) {
+ texMatrix.row4.X = x; texMatrix.row4.Y = y;
+ Gfx_LoadMatrix(2, &texMatrix);
+}
+
+void Gfx_DisableTextureOffset(void) { Gfx_LoadIdentityMatrix(2); }
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------State setup-------------------------------------------------------*
+*#########################################################################################################################*/
+static void Gfx_FreeState(void) { FreeDefaultResources(); }
+static void Gfx_RestoreState(void) {
+ InitDefaultResources();
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+ gfx_format = -1;
+
+ glHint(GL_FOG_HINT, GL_NICEST);
+ glAlphaFunc(GL_GREATER, 0.5f);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDepthFunc(GL_LEQUAL);
+}
+
+cc_bool Gfx_WarnIfNecessary(void) {
+ cc_string renderer = String_FromReadonly((const char*)glGetString(GL_RENDERER));
+
+#ifdef CC_BUILD_GL11
+ Chat_AddRaw("&cYou are using the very outdated OpenGL backend.");
+ Chat_AddRaw("&cAs such you may experience poor performance.");
+ Chat_AddRaw("&cIt is likely you need to install video card drivers.");
+#endif
+ if (!String_ContainsConst(&renderer, "Intel")) return false;
+
+ Chat_AddRaw("&cIntel graphics cards are known to have issues with the OpenGL build.");
+ Chat_AddRaw("&cVSync may not work, and you may see disappearing clouds and map edges.");
+#ifdef CC_BUILD_WIN
+ Chat_AddRaw("&cTry downloading the Direct3D 9 build instead.");
+#endif
+ return true;
+}
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------Compatibility-----------------------------------------------------*
+*#########################################################################################################################*/
+#ifdef CC_BUILD_GL11
+static void GLBackend_Init(void) { MakeIndices(gl_indices, GFX_MAX_INDICES); }
+#else
+/* fake vertex buffer objects with client side pointers */
+typedef struct fake_buffer { cc_uint8* data; } fake_buffer;
+static fake_buffer* cur_ib;
+static fake_buffer* cur_vb;
+#define fake_GetBuffer(target) (target == GL_ELEMENT_ARRAY_BUFFER ? &cur_ib : &cur_vb);
+
+static void APIENTRY fake_glBindBuffer(GLenum target, GLuint src) {
+ fake_buffer** buffer = fake_GetBuffer(target);
+ *buffer = (fake_buffer*)src;
+}
+
+static void APIENTRY fake_glDeleteBuffers(GLsizei n, const GLuint *buffers) {
+ Mem_Free((void*)buffers[0]);
+}
+
+static void APIENTRY fake_glGenBuffers(GLsizei n, GLuint *buffers) {
+ fake_buffer* buffer = (fake_buffer*)Mem_TryAlloc(1, sizeof(fake_buffer));
+ buffer->data = NULL;
+ buffers[0] = (GLuint)buffer;
+}
+
+static void APIENTRY fake_glBufferData(GLenum target, cc_uintptr size, const GLvoid* data, GLenum usage) {
+ fake_buffer* buffer = *fake_GetBuffer(target);
+ Mem_Free(buffer->data);
+
+ buffer->data = Mem_TryAlloc(size, 1);
+ if (data) Mem_Copy(buffer->data, data, size);
+}
+static void APIENTRY fake_glBufferSubData(GLenum target, cc_uintptr offset, cc_uintptr size, const GLvoid* data) {
+ fake_buffer* buffer = *fake_GetBuffer(target);
+ Mem_Copy(buffer->data, data, size);
+}
+
+static void GLBackend_Init(void) {
+ static const struct DynamicLibSym coreVboFuncs[5] = {
+ DynamicLib_Sym2("glBindBuffer", glBindBuffer), DynamicLib_Sym2("glDeleteBuffers", glDeleteBuffers),
+ DynamicLib_Sym2("glGenBuffers", glGenBuffers), DynamicLib_Sym2("glBufferData", glBufferData),
+ DynamicLib_Sym2("glBufferSubData", glBufferSubData)
+ };
+ static const struct DynamicLibSym arbVboFuncs[5] = {
+ DynamicLib_Sym2("glBindBufferARB", glBindBuffer), DynamicLib_Sym2("glDeleteBuffersARB", glDeleteBuffers),
+ DynamicLib_Sym2("glGenBuffersARB", glGenBuffers), DynamicLib_Sym2("glBufferDataARB", glBufferData),
+ DynamicLib_Sym2("glBufferSubDataARB", glBufferSubData)
+ };
+ static const cc_string vboExt = String_FromConst("GL_ARB_vertex_buffer_object");
+ cc_string extensions = String_FromReadonly((const char*)glGetString(GL_EXTENSIONS));
+ const GLubyte* ver = glGetString(GL_VERSION);
+
+ /* Version string is always: x.y. (and whatever afterwards) */
+ int major = ver[0] - '0', minor = ver[2] - '0';
+
+ /* Supported in core since 1.5 */
+ if (major > 1 || (major == 1 && minor >= 5)) {
+ GLContext_GetAll(coreVboFuncs, Array_Elems(coreVboFuncs));
+ } else if (String_CaselessContains(&extensions, &vboExt)) {
+ GLContext_GetAll(arbVboFuncs, Array_Elems(arbVboFuncs));
+ } else {
+ Logger_Abort("Only OpenGL 1.1 supported.\n\n" \
+ "Compile the game with CC_BUILD_GL11, or ask on the ClassiCube forums for it");
+
+ _glBindBuffer = fake_glBindBuffer; _glDeleteBuffers = fake_glDeleteBuffers;
+ _glGenBuffers = fake_glGenBuffers; _glBufferData = fake_glBufferData;
+ _glBufferSubData = fake_glBufferSubData;
+ }
+ customMipmapsLevels = true;
+}
+#endif
+
+
+/*########################################################################################################################*
+*----------------------------------------------------------Drawing--------------------------------------------------------*
+*#########################################################################################################################*/
+#ifdef CC_BUILD_GL11
+/* point to client side dynamic array */
+#define VB_PTR ((cc_uint8*)dynamicListData)
+#define IB_PTR gl_indices
+#else
+/* no client side array, use vertex buffer object */
+#define VB_PTR 0
+#define IB_PTR NULL
+#endif
+
+static void GL_SetupVbColoured(void) {
+ glVertexPointer(3, GL_FLOAT, SIZEOF_VERTEX_COLOURED, (void*)(VB_PTR + 0));
+ glColorPointer(4, GL_UNSIGNED_BYTE, SIZEOF_VERTEX_COLOURED, (void*)(VB_PTR + 12));
+}
+
+static void GL_SetupVbTextured(void) {
+ glVertexPointer(3, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + 0));
+ glColorPointer(4, GL_UNSIGNED_BYTE, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + 12));
+ glTexCoordPointer(2, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + 16));
+}
+
+static void GL_SetupVbColoured_Range(int startVertex) {
+ cc_uint32 offset = startVertex * SIZEOF_VERTEX_COLOURED;
+ glVertexPointer(3, GL_FLOAT, SIZEOF_VERTEX_COLOURED, (void*)(VB_PTR + offset));
+ glColorPointer(4, GL_UNSIGNED_BYTE, SIZEOF_VERTEX_COLOURED, (void*)(VB_PTR + offset + 12));
+}
+
+static void GL_SetupVbTextured_Range(int startVertex) {
+ cc_uint32 offset = startVertex * SIZEOF_VERTEX_TEXTURED;
+ glVertexPointer(3, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset));
+ glColorPointer(4, GL_UNSIGNED_BYTE, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset + 12));
+ glTexCoordPointer(2, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset + 16));
+}
+
+void Gfx_SetVertexFormat(VertexFormat fmt) {
+ if (fmt == gfx_format) return;
+ gfx_format = fmt;
+ gfx_stride = strideSizes[fmt];
+
+ if (fmt == VERTEX_FORMAT_TEXTURED) {
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ gfx_setupVBFunc = GL_SetupVbTextured;
+ gfx_setupVBRangeFunc = GL_SetupVbTextured_Range;
+ } else {
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ gfx_setupVBFunc = GL_SetupVbColoured;
+ gfx_setupVBRangeFunc = GL_SetupVbColoured_Range;
+ }
+}
+
+void Gfx_DrawVb_Lines(int verticesCount) {
+ gfx_setupVBFunc();
+ glDrawArrays(GL_LINES, 0, verticesCount);
+}
+
+void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) {
+#ifdef CC_BUILD_GL11
+ if (activeList != gl_DYNAMICLISTID) { glCallList(activeList); return; }
+#endif
+ gfx_setupVBRangeFunc(startVertex);
+ glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, IB_PTR);
+}
+
+void Gfx_DrawVb_IndexedTris(int verticesCount) {
+#ifdef CC_BUILD_GL11
+ if (activeList != gl_DYNAMICLISTID) { glCallList(activeList); return; }
+#endif
+ gfx_setupVBFunc();
+ glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, IB_PTR);
+}
+
+#ifdef CC_BUILD_GL11
+void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { glCallList(activeList); }
+#else
+void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) {
+ cc_uint32 offset = startVertex * SIZEOF_VERTEX_TEXTURED;
+ glVertexPointer(3, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset));
+ glColorPointer(4, GL_UNSIGNED_BYTE, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset + 12));
+ glTexCoordPointer(2, GL_FLOAT, SIZEOF_VERTEX_TEXTURED, (void*)(VB_PTR + offset + 16));
+ glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, IB_PTR);
+}
+#endif /* !CC_BUILD_GL11 */
+#endif
diff --git a/src/Graphics_GL2.c b/src/Graphics_GL2.c
new file mode 100644
index 000000000..3ff64947b
--- /dev/null
+++ b/src/Graphics_GL2.c
@@ -0,0 +1,566 @@
+#include "Core.h"
+#if defined CC_BUILD_GL && defined CC_BUILD_GLMODERN
+#include "_GraphicsBase.h"
+#include "Chat.h"
+#include "Errors.h"
+#include "Logger.h"
+#include "Window.h"
+/* OpenGL 2.0 backend (alternative modern-ish backend) */
+
+#if defined CC_BUILD_WIN
+/* Avoid pointless includes */
+#define WIN32_LEAN_AND_MEAN
+#define NOSERVICE
+#define NOMCX
+#define NOIME
+#include
+#include
+#elif defined CC_BUILD_IOS
+#include
+#elif defined CC_BUILD_MACOS
+#include
+#elif defined CC_BUILD_GLES
+#include
+#else
+#define GL_GLEXT_PROTOTYPES
+#include
+#endif
+
+#include "_GLShared.h"
+/* Current format and size of vertices */
+static int gfx_stride, gfx_format = -1;
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------Index buffers-----------------------------------------------------*
+*#########################################################################################################################*/
+static GLuint GL_GenAndBind(GLenum target) {
+ GLuint id;
+ glGenBuffers(1, &id);
+ glBindBuffer(target, id);
+ return id;
+}
+
+GfxResourceID Gfx_CreateIb(void* indices, int indicesCount) {
+ GLuint id = GL_GenAndBind(GL_ELEMENT_ARRAY_BUFFER);
+ cc_uint32 size = indicesCount * 2;
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, indices, GL_STATIC_DRAW);
+ return id;
+}
+
+void Gfx_BindIb(GfxResourceID ib) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)ib); }
+
+void Gfx_DeleteIb(GfxResourceID* ib) {
+ GLuint id = (GLuint)(*ib);
+ if (!id) return;
+ glDeleteBuffers(1, &id);
+ *ib = 0;
+}
+
+
+/*########################################################################################################################*
+*------------------------------------------------------Vertex buffers-----------------------------------------------------*
+*#########################################################################################################################*/
+GfxResourceID Gfx_CreateVb(VertexFormat fmt, int count) {
+ return GL_GenAndBind(GL_ARRAY_BUFFER);
+}
+
+void Gfx_BindVb(GfxResourceID vb) { glBindBuffer(GL_ARRAY_BUFFER, (GLuint)vb); }
+
+void Gfx_DeleteVb(GfxResourceID* vb) {
+ GLuint id = (GLuint)(*vb);
+ if (!id) return;
+ glDeleteBuffers(1, &id);
+ *vb = 0;
+}
+
+void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) {
+ return FastAllocTempMem(count * strideSizes[fmt]);
+}
+
+void Gfx_UnlockVb(GfxResourceID vb) {
+ glBufferData(GL_ARRAY_BUFFER, tmpSize, tmpData, GL_STATIC_DRAW);
+}
+
+
+/*########################################################################################################################*
+*--------------------------------------------------Dynamic vertex buffers-------------------------------------------------*
+*#########################################################################################################################*/
+GfxResourceID Gfx_CreateDynamicVb(VertexFormat fmt, int maxVertices) {
+ GLuint id;
+ cc_uint32 size;
+ if (Gfx.LostContext) return 0;
+
+ id = GL_GenAndBind(GL_ARRAY_BUFFER);
+ size = maxVertices * strideSizes[fmt];
+ glBufferData(GL_ARRAY_BUFFER, size, NULL, GL_DYNAMIC_DRAW);
+ return id;
+}
+
+void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) {
+ return FastAllocTempMem(count * strideSizes[fmt]);
+}
+
+void Gfx_UnlockDynamicVb(GfxResourceID vb) {
+ glBindBuffer(GL_ARRAY_BUFFER, (GLuint)vb);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, tmpSize, tmpData);
+}
+
+void Gfx_SetDynamicVbData(GfxResourceID vb, void* vertices, int vCount) {
+ cc_uint32 size = vCount * gfx_stride;
+ glBindBuffer(GL_ARRAY_BUFFER, (GLuint)vb);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, size, vertices);
+}
+
+
+/*########################################################################################################################*
+*------------------------------------------------------OpenGL modern------------------------------------------------------*
+*#########################################################################################################################*/
+#define FTR_TEXTURE_UV (1 << 0)
+#define FTR_ALPHA_TEST (1 << 1)
+#define FTR_TEX_OFFSET (1 << 2)
+#define FTR_LINEAR_FOG (1 << 3)
+#define FTR_DENSIT_FOG (1 << 4)
+#define FTR_HASANY_FOG (FTR_LINEAR_FOG | FTR_DENSIT_FOG)
+#define FTR_FS_MEDIUMP (1 << 7)
+
+#define UNI_MVP_MATRIX (1 << 0)
+#define UNI_TEX_OFFSET (1 << 1)
+#define UNI_FOG_COL (1 << 2)
+#define UNI_FOG_END (1 << 3)
+#define UNI_FOG_DENS (1 << 4)
+#define UNI_MASK_ALL 0x1F
+
+/* cached uniforms (cached for multiple programs */
+static struct Matrix _view, _proj, _mvp;
+static cc_bool gfx_alphaTest, gfx_texTransform;
+static float _texX, _texY;
+static PackedCol gfx_fogColor;
+static float gfx_fogEnd = -1.0f, gfx_fogDensity = -1.0f;
+static int gfx_fogMode = -1;
+
+/* shader programs (emulate fixed function) */
+static struct GLShader {
+ int features; /* what features are enabled for this shader */
+ int uniforms; /* which associated uniforms need to be resent to GPU */
+ GLuint program; /* OpenGL program ID (0 if not yet compiled) */
+ int locations[5]; /* location of uniforms (not constant) */
+} shaders[6 * 3] = {
+ /* no fog */
+ { 0 },
+ { 0 | FTR_ALPHA_TEST },
+ { FTR_TEXTURE_UV },
+ { FTR_TEXTURE_UV | FTR_ALPHA_TEST },
+ { FTR_TEXTURE_UV | FTR_TEX_OFFSET },
+ { FTR_TEXTURE_UV | FTR_TEX_OFFSET | FTR_ALPHA_TEST },
+ /* linear fog */
+ { FTR_LINEAR_FOG | 0 },
+ { FTR_LINEAR_FOG | 0 | FTR_ALPHA_TEST },
+ { FTR_LINEAR_FOG | FTR_TEXTURE_UV },
+ { FTR_LINEAR_FOG | FTR_TEXTURE_UV | FTR_ALPHA_TEST },
+ { FTR_LINEAR_FOG | FTR_TEXTURE_UV | FTR_TEX_OFFSET },
+ { FTR_LINEAR_FOG | FTR_TEXTURE_UV | FTR_TEX_OFFSET | FTR_ALPHA_TEST },
+ /* density fog */
+ { FTR_DENSIT_FOG | 0 },
+ { FTR_DENSIT_FOG | 0 | FTR_ALPHA_TEST },
+ { FTR_DENSIT_FOG | FTR_TEXTURE_UV },
+ { FTR_DENSIT_FOG | FTR_TEXTURE_UV | FTR_ALPHA_TEST },
+ { FTR_DENSIT_FOG | FTR_TEXTURE_UV | FTR_TEX_OFFSET },
+ { FTR_DENSIT_FOG | FTR_TEXTURE_UV | FTR_TEX_OFFSET | FTR_ALPHA_TEST },
+};
+static struct GLShader* gfx_activeShader;
+
+/* Generates source code for a GLSL vertex shader, based on shader's flags */
+static void GenVertexShader(const struct GLShader* shader, cc_string* dst) {
+ int uv = shader->features & FTR_TEXTURE_UV;
+ int tm = shader->features & FTR_TEX_OFFSET;
+
+ String_AppendConst(dst, "attribute vec3 in_pos;\n");
+ String_AppendConst(dst, "attribute vec4 in_col;\n");
+ if (uv) String_AppendConst(dst, "attribute vec2 in_uv;\n");
+ String_AppendConst(dst, "varying vec4 out_col;\n");
+ if (uv) String_AppendConst(dst, "varying vec2 out_uv;\n");
+ String_AppendConst(dst, "uniform mat4 mvp;\n");
+ if (tm) String_AppendConst(dst, "uniform vec2 texOffset;\n");
+
+ String_AppendConst(dst, "void main() {\n");
+ String_AppendConst(dst, " gl_Position = mvp * vec4(in_pos, 1.0);\n");
+ String_AppendConst(dst, " out_col = in_col;\n");
+ if (uv) String_AppendConst(dst, " out_uv = in_uv;\n");
+ if (tm) String_AppendConst(dst, " out_uv = out_uv + texOffset;\n");
+ String_AppendConst(dst, "}");
+}
+
+/* Generates source code for a GLSL fragment shader, based on shader's flags */
+static void GenFragmentShader(const struct GLShader* shader, cc_string* dst) {
+ int uv = shader->features & FTR_TEXTURE_UV;
+ int al = shader->features & FTR_ALPHA_TEST;
+ int fl = shader->features & FTR_LINEAR_FOG;
+ int fd = shader->features & FTR_DENSIT_FOG;
+ int fm = shader->features & FTR_HASANY_FOG;
+
+#ifdef CC_BUILD_GLES
+ int mp = shader->features & FTR_FS_MEDIUMP;
+ if (mp) String_AppendConst(dst, "precision mediump float;\n");
+ else String_AppendConst(dst, "precision highp float;\n");
+#endif
+
+ String_AppendConst(dst, "varying vec4 out_col;\n");
+ if (uv) String_AppendConst(dst, "varying vec2 out_uv;\n");
+ if (uv) String_AppendConst(dst, "uniform sampler2D texImage;\n");
+ if (fm) String_AppendConst(dst, "uniform vec3 fogCol;\n");
+ if (fl) String_AppendConst(dst, "uniform float fogEnd;\n");
+ if (fd) String_AppendConst(dst, "uniform float fogDensity;\n");
+
+ String_AppendConst(dst, "void main() {\n");
+ if (uv) String_AppendConst(dst, " vec4 col = texture2D(texImage, out_uv) * out_col;\n");
+ else String_AppendConst(dst, " vec4 col = out_col;\n");
+ if (al) String_AppendConst(dst, " if (col.a < 0.5) discard;\n");
+ if (fm) String_AppendConst(dst, " float depth = gl_FragCoord.z / gl_FragCoord.w;\n");
+ if (fl) String_AppendConst(dst, " float f = clamp((fogEnd - depth) / fogEnd, 0.0, 1.0);\n");
+ if (fd) String_AppendConst(dst, " float f = clamp(exp(fogDensity * depth), 0.0, 1.0);\n");
+ if (fm) String_AppendConst(dst, " col.rgb = mix(fogCol, col.rgb, f);\n");
+ String_AppendConst(dst, " gl_FragColor = col;\n");
+ String_AppendConst(dst, "}");
+}
+
+/* Tries to compile GLSL shader code */
+static GLint CompileShader(GLint shader, const cc_string* src) {
+ const char* str = src->buffer;
+ int len = src->length;
+ GLint temp;
+
+ glShaderSource(shader, 1, &str, &len);
+ glCompileShader(shader);
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &temp);
+ return temp;
+}
+
+/* Logs information then aborts program */
+static void ShaderFailed(GLint shader) {
+ char logInfo[2048];
+ GLint temp;
+ if (!shader) Logger_Abort("Failed to create shader");
+
+ temp = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &temp);
+
+ if (temp > 1) {
+ glGetShaderInfoLog(shader, 2047, NULL, logInfo);
+ logInfo[2047] = '\0';
+ Window_ShowDialog("Failed to compile shader", logInfo);
+ }
+ Logger_Abort("Failed to compile shader");
+}
+
+/* Tries to compile vertex and fragment shaders, then link into an OpenGL program */
+static void CompileProgram(struct GLShader* shader) {
+ char tmpBuffer[2048]; cc_string tmp;
+ GLuint vs, fs, program;
+ GLint temp;
+
+ vs = glCreateShader(GL_VERTEX_SHADER);
+ if (!vs) { Platform_LogConst("Failed to create vertex shader"); return; }
+
+ String_InitArray(tmp, tmpBuffer);
+ GenVertexShader(shader, &tmp);
+ if (!CompileShader(vs, &tmp)) ShaderFailed(vs);
+
+ fs = glCreateShader(GL_FRAGMENT_SHADER);
+ if (!fs) { Platform_LogConst("Failed to create fragment shader"); glDeleteShader(vs); return; }
+
+ tmp.length = 0;
+ GenFragmentShader(shader, &tmp);
+ if (!CompileShader(fs, &tmp)) {
+ /* Sometimes fails 'highp precision is not supported in fragment shader' */
+ /* So try compiling shader again without highp precision */
+ shader->features |= FTR_FS_MEDIUMP;
+
+ tmp.length = 0;
+ GenFragmentShader(shader, &tmp);
+ if (!CompileShader(fs, &tmp)) ShaderFailed(fs);
+ }
+
+
+ program = glCreateProgram();
+ if (!program) Logger_Abort("Failed to create program");
+ shader->program = program;
+
+ glAttachShader(program, vs);
+ glAttachShader(program, fs);
+
+ /* Force in_pos/in_col/in_uv attributes to be bound to 0,1,2 locations */
+ /* Although most browsers assign the attributes in this order anyways, */
+ /* the specification does not require this. (e.g. Safari doesn't) */
+ glBindAttribLocation(program, 0, "in_pos");
+ glBindAttribLocation(program, 1, "in_col");
+ glBindAttribLocation(program, 2, "in_uv");
+
+ glLinkProgram(program);
+ glGetProgramiv(program, GL_LINK_STATUS, &temp);
+
+ if (temp) {
+ glDetachShader(program, vs);
+ glDetachShader(program, fs);
+
+ glDeleteShader(vs);
+ glDeleteShader(fs);
+
+ shader->locations[0] = glGetUniformLocation(program, "mvp");
+ shader->locations[1] = glGetUniformLocation(program, "texOffset");
+ shader->locations[2] = glGetUniformLocation(program, "fogCol");
+ shader->locations[3] = glGetUniformLocation(program, "fogEnd");
+ shader->locations[4] = glGetUniformLocation(program, "fogDensity");
+ return;
+ }
+ temp = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &temp);
+
+ if (temp > 0) {
+ glGetProgramInfoLog(program, 2047, NULL, tmpBuffer);
+ tmpBuffer[2047] = '\0';
+ Window_ShowDialog("Failed to compile program", tmpBuffer);
+ }
+ Logger_Abort("Failed to compile program");
+}
+
+/* Marks a uniform as changed on all programs */
+static void DirtyUniform(int uniform) {
+ int i;
+ for (i = 0; i < Array_Elems(shaders); i++) {
+ shaders[i].uniforms |= uniform;
+ }
+}
+
+/* Sends changed uniforms to the GPU for current program */
+static void ReloadUniforms(void) {
+ struct GLShader* s = gfx_activeShader;
+ if (!s) return; /* NULL if context is lost */
+
+ if (s->uniforms & UNI_MVP_MATRIX) {
+ glUniformMatrix4fv(s->locations[0], 1, false, (float*)&_mvp);
+ s->uniforms &= ~UNI_MVP_MATRIX;
+ }
+ if ((s->uniforms & UNI_TEX_OFFSET) && (s->features & FTR_TEX_OFFSET)) {
+ glUniform2f(s->locations[1], _texX, _texY);
+ s->uniforms &= ~UNI_TEX_OFFSET;
+ }
+ if ((s->uniforms & UNI_FOG_COL) && (s->features & FTR_HASANY_FOG)) {
+ glUniform3f(s->locations[2], PackedCol_R(gfx_fogColor) / 255.0f, PackedCol_G(gfx_fogColor) / 255.0f,
+ PackedCol_B(gfx_fogColor) / 255.0f);
+ s->uniforms &= ~UNI_FOG_COL;
+ }
+ if ((s->uniforms & UNI_FOG_END) && (s->features & FTR_LINEAR_FOG)) {
+ glUniform1f(s->locations[3], gfx_fogEnd);
+ s->uniforms &= ~UNI_FOG_END;
+ }
+ if ((s->uniforms & UNI_FOG_DENS) && (s->features & FTR_DENSIT_FOG)) {
+ /* See https://docs.microsoft.com/en-us/previous-versions/ms537113(v%3Dvs.85) */
+ /* The equation for EXP mode is exp(-density * z), so just negate density here */
+ glUniform1f(s->locations[4], -gfx_fogDensity);
+ s->uniforms &= ~UNI_FOG_DENS;
+ }
+}
+
+/* Switches program to one that duplicates current fixed function state */
+/* Compiles program and reloads uniforms if needed */
+static void SwitchProgram(void) {
+ struct GLShader* shader;
+ int index = 0;
+
+ if (gfx_fogEnabled) {
+ index += 6; /* linear fog */
+ if (gfx_fogMode >= 1) index += 6; /* exp fog */
+ }
+
+ if (gfx_format == VERTEX_FORMAT_TEXTURED) index += 2;
+ if (gfx_texTransform) index += 2;
+ if (gfx_alphaTest) index += 1;
+
+ shader = &shaders[index];
+ if (shader == gfx_activeShader) { ReloadUniforms(); return; }
+ if (!shader->program) CompileProgram(shader);
+
+ gfx_activeShader = shader;
+ glUseProgram(shader->program);
+ ReloadUniforms();
+}
+
+
+/*########################################################################################################################*
+*-----------------------------------------------------State management----------------------------------------------------*
+*#########################################################################################################################*/
+void Gfx_SetFog(cc_bool enabled) { gfx_fogEnabled = enabled; SwitchProgram(); }
+void Gfx_SetFogCol(PackedCol col) {
+ if (col == gfx_fogColor) return;
+ gfx_fogColor = col;
+ DirtyUniform(UNI_FOG_COL);
+ ReloadUniforms();
+}
+
+void Gfx_SetFogDensity(float value) {
+ if (gfx_fogDensity == value) return;
+ gfx_fogDensity = value;
+ DirtyUniform(UNI_FOG_DENS);
+ ReloadUniforms();
+}
+
+void Gfx_SetFogEnd(float value) {
+ if (gfx_fogEnd == value) return;
+ gfx_fogEnd = value;
+ DirtyUniform(UNI_FOG_END);
+ ReloadUniforms();
+}
+
+void Gfx_SetFogMode(FogFunc func) {
+ if (gfx_fogMode == func) return;
+ gfx_fogMode = func;
+ SwitchProgram();
+}
+
+void Gfx_SetTexturing(cc_bool enabled) { }
+void Gfx_SetAlphaTest(cc_bool enabled) { gfx_alphaTest = enabled; SwitchProgram(); }
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Matrices--------------------------------------------------------*
+*#########################################################################################################################*/
+void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) {
+ if (type == MATRIX_VIEW) _view = *matrix;
+ if (type == MATRIX_PROJECTION) _proj = *matrix;
+
+ Matrix_Mul(&_mvp, &_view, &_proj);
+ DirtyUniform(UNI_MVP_MATRIX);
+ ReloadUniforms();
+}
+void Gfx_LoadIdentityMatrix(MatrixType type) {
+ Gfx_LoadMatrix(type, &Matrix_Identity);
+}
+
+void Gfx_EnableTextureOffset(float x, float y) {
+ _texX = x; _texY = y;
+ gfx_texTransform = true;
+ DirtyUniform(UNI_TEX_OFFSET);
+ SwitchProgram();
+}
+
+void Gfx_DisableTextureOffset(void) {
+ gfx_texTransform = false;
+ SwitchProgram();
+}
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------State setup-------------------------------------------------------*
+*#########################################################################################################################*/
+static void GLBackend_Init(void) {
+#ifndef CC_BUILD_GLES
+ customMipmapsLevels = true;
+#endif
+}
+
+static void Gfx_FreeState(void) {
+ int i;
+ FreeDefaultResources();
+ gfx_activeShader = NULL;
+
+ for (i = 0; i < Array_Elems(shaders); i++) {
+ glDeleteProgram(shaders[i].program);
+ shaders[i].program = 0;
+ }
+}
+
+static void Gfx_RestoreState(void) {
+ InitDefaultResources();
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+ gfx_format = -1;
+
+ DirtyUniform(UNI_MASK_ALL);
+ GL_ClearCol(gfx_clearColor);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDepthFunc(GL_LEQUAL);
+}
+cc_bool Gfx_WarnIfNecessary(void) { return false; }
+
+
+/*########################################################################################################################*
+*----------------------------------------------------------Drawing--------------------------------------------------------*
+*#########################################################################################################################*/
+typedef void (*GL_SetupVBFunc)(void);
+typedef void (*GL_SetupVBRangeFunc)(int startVertex);
+static GL_SetupVBFunc gfx_setupVBFunc;
+static GL_SetupVBRangeFunc gfx_setupVBRangeFunc;
+
+static void GL_SetupVbColoured(void) {
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, SIZEOF_VERTEX_COLOURED, (void*)0);
+ glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, SIZEOF_VERTEX_COLOURED, (void*)12);
+}
+
+static void GL_SetupVbTextured(void) {
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, SIZEOF_VERTEX_TEXTURED, (void*)0);
+ glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, SIZEOF_VERTEX_TEXTURED, (void*)12);
+ glVertexAttribPointer(2, 2, GL_FLOAT, false, SIZEOF_VERTEX_TEXTURED, (void*)16);
+}
+
+static void GL_SetupVbColoured_Range(int startVertex) {
+ cc_uint32 offset = startVertex * SIZEOF_VERTEX_COLOURED;
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, SIZEOF_VERTEX_COLOURED, (void*)(offset));
+ glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, SIZEOF_VERTEX_COLOURED, (void*)(offset + 12));
+}
+
+static void GL_SetupVbTextured_Range(int startVertex) {
+ cc_uint32 offset = startVertex * SIZEOF_VERTEX_TEXTURED;
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, SIZEOF_VERTEX_TEXTURED, (void*)(offset));
+ glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, SIZEOF_VERTEX_TEXTURED, (void*)(offset + 12));
+ glVertexAttribPointer(2, 2, GL_FLOAT, false, SIZEOF_VERTEX_TEXTURED, (void*)(offset + 16));
+}
+
+void Gfx_SetVertexFormat(VertexFormat fmt) {
+ if (fmt == gfx_format) return;
+ gfx_format = fmt;
+ gfx_stride = strideSizes[fmt];
+
+ if (fmt == VERTEX_FORMAT_TEXTURED) {
+ glEnableVertexAttribArray(2);
+ gfx_setupVBFunc = GL_SetupVbTextured;
+ gfx_setupVBRangeFunc = GL_SetupVbTextured_Range;
+ } else {
+ glDisableVertexAttribArray(2);
+ gfx_setupVBFunc = GL_SetupVbColoured;
+ gfx_setupVBRangeFunc = GL_SetupVbColoured_Range;
+ }
+ SwitchProgram();
+}
+
+void Gfx_DrawVb_Lines(int verticesCount) {
+ gfx_setupVBFunc();
+ glDrawArrays(GL_LINES, 0, verticesCount);
+}
+
+void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) {
+ gfx_setupVBRangeFunc(startVertex);
+ glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, NULL);
+}
+
+void Gfx_DrawVb_IndexedTris(int verticesCount) {
+ gfx_setupVBFunc();
+ glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, NULL);
+}
+
+void Gfx_BindVb_Textured(GfxResourceID vb) {
+ Gfx_BindVb(vb);
+ GL_SetupVbTextured();
+}
+
+void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) {
+ if (startVertex + verticesCount > GFX_MAX_VERTICES) {
+ GL_SetupVbTextured_Range(startVertex);
+ glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, NULL);
+ GL_SetupVbTextured();
+ } else {
+ /* ICOUNT(startVertex) * 2 = startVertex * 3 */
+ glDrawElements(GL_TRIANGLES, ICOUNT(verticesCount), GL_UNSIGNED_SHORT, (void*)(startVertex * 3));
+ }
+}
+#endif
diff --git a/src/_GLShared.h b/src/_GLShared.h
new file mode 100644
index 000000000..1f38411b8
--- /dev/null
+++ b/src/_GLShared.h
@@ -0,0 +1,285 @@
+#define _GL_TEXTURE_MAX_LEVEL 0x813D
+#define _GL_BGRA_EXT 0x80E1
+#define _GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
+
+#if defined CC_BUILD_WEB || defined CC_BUILD_ANDROID
+#define PIXEL_FORMAT GL_RGBA
+#else
+#define PIXEL_FORMAT _GL_BGRA_EXT
+#endif
+
+#if defined CC_BIG_ENDIAN
+/* Pixels are stored in memory as A,R,G,B but GL_UNSIGNED_BYTE will interpret as B,G,R,A */
+/* So use GL_UNSIGNED_INT_8_8_8_8_REV instead to remedy this */
+#define TRANSFER_FORMAT _GL_UNSIGNED_INT_8_8_8_8_REV
+#else
+/* Pixels are stored in memory as B,G,R,A and GL_UNSIGNED_BYTE will interpret as B,G,R,A */
+/* So fine to just use GL_UNSIGNED_BYTE here */
+#define TRANSFER_FORMAT GL_UNSIGNED_BYTE
+#endif
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------General---------------------------------------------------------*
+*#########################################################################################################################*/
+static void GL_UpdateVsync(void) {
+ GLContext_SetFpsLimit(gfx_vsync, gfx_minFrameMs);
+}
+
+static void GLBackend_Init(void);
+void Gfx_Create(void) {
+ GLContext_Create();
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &Gfx.MaxTexWidth);
+ Gfx.MaxTexHeight = Gfx.MaxTexWidth;
+ Gfx.Created = true;
+
+ GLBackend_Init();
+ Gfx_RestoreState();
+ GL_UpdateVsync();
+}
+
+cc_bool Gfx_TryRestoreContext(void) {
+ return GLContext_TryRestore();
+}
+
+void Gfx_Free(void) {
+ Gfx_FreeState();
+ GLContext_Free();
+}
+
+#define gl_Toggle(cap) if (enabled) { glEnable(cap); } else { glDisable(cap); }
+static void* tmpData;
+static int tmpSize;
+
+static void* FastAllocTempMem(int size) {
+ if (size > tmpSize) {
+ Mem_Free(tmpData);
+ tmpData = Mem_Alloc(size, 1, "Gfx_AllocTempMemory");
+ }
+
+ tmpSize = size;
+ return tmpData;
+}
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Textures--------------------------------------------------------*
+*#########################################################################################################################*/
+static void Gfx_DoMipmaps(int x, int y, struct Bitmap* bmp, int rowWidth, cc_bool partial) {
+ BitmapCol* prev = bmp->scan0;
+ BitmapCol* cur;
+
+ int lvls = CalcMipmapsLevels(bmp->width, bmp->height);
+ int lvl, width = bmp->width, height = bmp->height;
+
+ for (lvl = 1; lvl <= lvls; lvl++) {
+ x /= 2; y /= 2;
+ if (width > 1) width /= 2;
+ if (height > 1) height /= 2;
+
+ cur = (BitmapCol*)Mem_Alloc(width * height, 4, "mipmaps");
+ GenMipmaps(width, height, cur, prev, rowWidth);
+
+ if (partial) {
+ glTexSubImage2D(GL_TEXTURE_2D, lvl, x, y, width, height, PIXEL_FORMAT, TRANSFER_FORMAT, cur);
+ } else {
+ glTexImage2D(GL_TEXTURE_2D, lvl, GL_RGBA, width, height, 0, PIXEL_FORMAT, TRANSFER_FORMAT, cur);
+ }
+
+ if (prev != bmp->scan0) Mem_Free(prev);
+ prev = cur;
+ rowWidth = width;
+ }
+ if (prev != bmp->scan0) Mem_Free(prev);
+}
+
+GfxResourceID Gfx_CreateTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) {
+ GLuint texId;
+ glGenTextures(1, &texId);
+ glBindTexture(GL_TEXTURE_2D, texId);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ if (!Math_IsPowOf2(bmp->width) || !Math_IsPowOf2(bmp->height)) {
+ Logger_Abort("Textures must have power of two dimensions");
+ }
+ if (Gfx.LostContext) return 0;
+
+ if (mipmaps) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
+ if (customMipmapsLevels) {
+ int lvls = CalcMipmapsLevels(bmp->width, bmp->height);
+ glTexParameteri(GL_TEXTURE_2D, _GL_TEXTURE_MAX_LEVEL, lvls);
+ }
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bmp->width, bmp->height, 0, PIXEL_FORMAT, TRANSFER_FORMAT, bmp->scan0);
+
+ if (mipmaps) Gfx_DoMipmaps(0, 0, bmp, bmp->width, false);
+ return texId;
+}
+
+#define UPDATE_FAST_SIZE (64 * 64)
+static CC_NOINLINE void UpdateTextureSlow(int x, int y, struct Bitmap* part, int rowWidth) {
+ BitmapCol buffer[UPDATE_FAST_SIZE];
+ void* ptr = (void*)buffer;
+ int count = part->width * part->height;
+
+ /* cannot allocate memory on the stack for very big updates */
+ if (count > UPDATE_FAST_SIZE) {
+ ptr = Mem_Alloc(count, 4, "Gfx_UpdateTexture temp");
+ }
+
+ CopyTextureData(ptr, part->width << 2, part, rowWidth << 2);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, part->width, part->height, PIXEL_FORMAT, TRANSFER_FORMAT, ptr);
+ if (count > UPDATE_FAST_SIZE) Mem_Free(ptr);
+}
+
+void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, int rowWidth, cc_bool mipmaps) {
+ glBindTexture(GL_TEXTURE_2D, (GLuint)texId);
+ /* TODO: Use GL_UNPACK_ROW_LENGTH for Desktop OpenGL */
+
+ if (part->width == rowWidth) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, part->width, part->height, PIXEL_FORMAT, TRANSFER_FORMAT, part->scan0);
+ } else {
+ UpdateTextureSlow(x, y, part, rowWidth);
+ }
+ if (mipmaps) Gfx_DoMipmaps(x, y, part, rowWidth, true);
+}
+
+void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) {
+ Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps);
+}
+
+void Gfx_BindTexture(GfxResourceID texId) {
+ glBindTexture(GL_TEXTURE_2D, (GLuint)texId);
+}
+
+void Gfx_DeleteTexture(GfxResourceID* texId) {
+ GLuint id = (GLuint)(*texId);
+ if (!id) return;
+ glDeleteTextures(1, &id);
+ *texId = 0;
+}
+
+void Gfx_EnableMipmaps(void) { }
+void Gfx_DisableMipmaps(void) { }
+
+
+/*########################################################################################################################*
+*-----------------------------------------------------State management----------------------------------------------------*
+*#########################################################################################################################*/
+static PackedCol gfx_clearColor;
+void Gfx_SetFaceCulling(cc_bool enabled) { gl_Toggle(GL_CULL_FACE); }
+void Gfx_SetAlphaBlending(cc_bool enabled) { gl_Toggle(GL_BLEND); }
+void Gfx_SetAlphaArgBlend(cc_bool enabled) { }
+
+static void GL_ClearCol(PackedCol col) {
+ glClearColor(PackedCol_R(col) / 255.0f, PackedCol_G(col) / 255.0f,
+ PackedCol_B(col) / 255.0f, PackedCol_A(col) / 255.0f);
+}
+void Gfx_ClearCol(PackedCol col) {
+ if (col == gfx_clearColor) return;
+ GL_ClearCol(col);
+ gfx_clearColor = col;
+}
+
+void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) {
+ glColorMask(r, g, b, a);
+}
+
+void Gfx_SetDepthWrite(cc_bool enabled) { glDepthMask(enabled); }
+void Gfx_SetDepthTest(cc_bool enabled) { gl_Toggle(GL_DEPTH_TEST); }
+
+
+/*########################################################################################################################*
+*---------------------------------------------------------Matrices--------------------------------------------------------*
+*#########################################################################################################################*/
+void Gfx_CalcOrthoMatrix(float width, float height, struct Matrix* matrix) {
+ Matrix_Orthographic(matrix, 0.0f, width, 0.0f, height, ORTHO_NEAR, ORTHO_FAR);
+}
+void Gfx_CalcPerspectiveMatrix(float fov, float aspect, float zFar, struct Matrix* matrix) {
+ float zNear = 0.1f;
+ Matrix_PerspectiveFieldOfView(matrix, fov, aspect, zNear, zFar);
+}
+
+
+/*########################################################################################################################*
+*-----------------------------------------------------------Misc----------------------------------------------------------*
+*#########################################################################################################################*/
+static BitmapCol* GL_GetRow(struct Bitmap* bmp, int y) {
+ /* OpenGL stores bitmap in bottom-up order, so flip order when saving */
+ return Bitmap_GetRow(bmp, (bmp->height - 1) - y);
+}
+cc_result Gfx_TakeScreenshot(struct Stream* output) {
+ struct Bitmap bmp;
+ cc_result res;
+ GLint vp[4];
+
+ glGetIntegerv(GL_VIEWPORT, vp); /* { x, y, width, height } */
+ bmp.width = vp[2];
+ bmp.height = vp[3];
+
+ bmp.scan0 = (BitmapCol*)Mem_TryAlloc(bmp.width * bmp.height, 4);
+ if (!bmp.scan0) return ERR_OUT_OF_MEMORY;
+ glReadPixels(0, 0, bmp.width, bmp.height, PIXEL_FORMAT, TRANSFER_FORMAT, bmp.scan0);
+
+ res = Png_Encode(&bmp, output, GL_GetRow, false);
+ Mem_Free(bmp.scan0);
+ return res;
+}
+
+static void AppendVRAMStats(cc_string* info) {
+ static const cc_string memExt = String_FromConst("GL_NVX_gpu_memory_info");
+ GLint totalKb, curKb;
+ float total, cur;
+
+ /* NOTE: glGetString returns UTF8, but I just treat it as code page 437 */
+ cc_string exts = String_FromReadonly((const char*)glGetString(GL_EXTENSIONS));
+ if (!String_CaselessContains(&exts, &memExt)) return;
+
+ glGetIntegerv(0x9048, &totalKb);
+ glGetIntegerv(0x9049, &curKb);
+ if (totalKb <= 0 || curKb <= 0) return;
+
+ total = totalKb / 1024.0f; cur = curKb / 1024.0f;
+ String_Format2(info, "Video memory: %f2 MB total, %f2 free\n", &total, &cur);
+}
+
+void Gfx_GetApiInfo(cc_string* info) {
+ GLint depthBits;
+ int pointerSize = sizeof(void*) * 8;
+
+ glGetIntegerv(GL_DEPTH_BITS, &depthBits);
+ String_Format1(info, "-- Using OpenGL (%i bit) --\n", &pointerSize);
+ String_Format1(info, "Vendor: %c\n", glGetString(GL_VENDOR));
+ String_Format1(info, "Renderer: %c\n", glGetString(GL_RENDERER));
+ String_Format1(info, "GL version: %c\n", glGetString(GL_VERSION));
+ AppendVRAMStats(info);
+ String_Format2(info, "Max texture size: (%i, %i)\n", &Gfx.MaxTexWidth, &Gfx.MaxTexHeight);
+ String_Format1(info, "Depth buffer bits: %i\n", &depthBits);
+ GLContext_GetApiInfo(info);
+}
+
+void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) {
+ gfx_minFrameMs = minFrameMs;
+ gfx_vsync = vsync;
+ if (Gfx.Created) GL_UpdateVsync();
+}
+
+void Gfx_BeginFrame(void) { frameStart = Stopwatch_Measure(); }
+void Gfx_Clear(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); }
+
+void Gfx_EndFrame(void) {
+ if (!GLContext_SwapBuffers()) Gfx_LoseContext("GLContext lost");
+ if (gfx_minFrameMs) LimitFPS();
+}
+
+void Gfx_OnWindowResize(void) {
+ GLContext_Update();
+ /* In case GLContext_Update changes window bounds */
+ /* TODO: Eliminate this nasty hack.. */
+ Game_UpdateDimensions();
+ glViewport(0, 0, Game.Width, Game.Height);
+}