ref: eb62a80956b485c780f9028cd08440b4971aecba
parent: 4c024b540f755ca4679f826026e4843328cccb9e
author: Clownacy <Clownacy@users.noreply.github.com>
date: Wed Jul 31 18:38:33 EDT 2019
Change references to OpenGL 2.1 to 3.2
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -11,7 +11,7 @@
option(FIX_BUGS "Fix certain bugs (see src/Bug Fixes.txt)" OFF)
option(NONPORTABLE "Enable bits of code that aren't portable, but are what the original game used" OFF)
option(FORCE_LOCAL_LIBS "Compile the built-in versions of SDL2, FreeType, and FLTK instead of using the system-provided ones" OFF)
-set(RENDERER "Texture" CACHE STRING "Which renderer the game should use: 'OpenGL2' for an OpenGL 2.1 renderer, 'Texture' for SDL2's hardware-accelerated Texture API, 'Surface' for SDL2's software-rendered Surface API, and 'Software' for a handwritten software renderer")
+set(RENDERER "Texture" CACHE STRING "Which renderer the game should use: 'OpenGL3' for an OpenGL 3.2 renderer, 'Texture' for SDL2's hardware-accelerated Texture API, 'Surface' for SDL2's software-rendered Surface API, and 'Software' for a handwritten software renderer")
project(CSE2 LANGUAGES C CXX)
@@ -246,8 +246,8 @@
target_compile_definitions(CSE2 PRIVATE NONPORTABLE)
endif()
-if(RENDERER MATCHES "OpenGL2")
- target_sources(CSE2 PRIVATE "src/Backends/Rendering/OpenGL2.cpp")
+if(RENDERER MATCHES "OpenGL3")
+ target_sources(CSE2 PRIVATE "src/Backends/Rendering/OpenGL3.cpp")
find_package(GLEW REQUIRED)
target_link_libraries(CSE2 GLEW::GLEW)
find_package(OpenGL REQUIRED)
--- a/Makefile
+++ b/Makefile
@@ -208,8 +208,8 @@
RESOURCES += ICON/ICON_MINI.bmp
endif
-ifeq ($(RENDERER), OpenGL2)
- SOURCES += Backends/Rendering/OpenGL2
+ifeq ($(RENDERER), OpenGL3)
+ SOURCES += Backends/Rendering/OpenGL3
CXXFLAGS += `pkg-config glew --cflags`
ifeq ($(STATIC), 1)
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@
* `-DFIX_BUGS=ON` - Fix bugs in the game (see [src/Bug Fixes.txt](src/Bug%20Fixes.txt))
* `-DNONPORTABLE=ON` - Enable bits of code that aren't portable, but are what the original game used
* `-DFORCE_LOCAL_LIBS=ON` - Compile the built-in versions of SDL2, FreeType, and FLTK instead of using the system-provided ones
-* `-DRENDERER=OpenGL2` - Use the hardware-accelerated OpenGL 2.1 renderer
+* `-DRENDERER=OpenGL3` - Use the hardware-accelerated OpenGL 3.2 renderer
* `-DRENDERER=Texture` - Use the hardware-accelerated SDL2 Texture API renderer (default)
* `-DRENDERER=Surface` - Use the software-rendered SDL2 Surface API renderer
* `-DRENDERER=Software` - Use a handwritten software renderer
@@ -58,7 +58,7 @@
* `WINDOWS=1` - Enable Windows-only features like a unique file/taskbar icon, and system font loading (needed for the font setting in Config.dat to do anything)
* `RASPBERRY_PI=1` - Enable tweaks to improve performance on Raspberry Pis
* `NONPORTABLE=1` - Enable bits of code that aren't portable, but are what the original game used
-* `RENDERER=OpenGL2` - Use the hardware-accelerated OpenGL 2.1 renderer
+* `RENDERER=OpenGL3` - Use the hardware-accelerated OpenGL 3.2 renderer
* `RENDERER=Texture` - Use the hardware-accelerated SDL2 Texture API renderer (default)
* `RENDERER=Surface` - Use the software-rendered SDL2 Surface API renderer
* `RENDERER=Software` - Use a hand-written software renderer
--- a/src/Backends/Rendering/OpenGL2.cpp
+++ /dev/null
@@ -1,637 +1,0 @@
-#include "../Rendering.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-
-#include <GL/glew.h>
-#include "SDL.h"
-
-#include "../../WindowsWrapper.h"
-
-#include "../../Font.h"
-
-typedef struct Backend_Surface
-{
- GLuint texture_id;
- unsigned int width;
- unsigned int height;
- unsigned char *pixels;
-} Backend_Surface;
-
-typedef struct Backend_Glyph
-{
- GLuint texture_id;
- unsigned int width;
- unsigned int height;
-} Backend_Glyph;
-
-static SDL_Window *window;
-static SDL_GLContext context;
-
-static GLuint program_texture;
-static GLuint program_texture_colour_key;
-static GLuint program_colour_fill;
-static GLuint program_glyph;
-
-static GLint program_colour_fill_uniform_colour;
-static GLint program_glyph_uniform_colour;
-
-static GLuint framebuffer_id;
-static GLuint vertex_buffer_id;
-
-static struct
-{
- GLfloat vertexes[4][2];
- GLfloat texture_coordinates[4][2];
-} vertex_buffer;
-
-static Backend_Surface framebuffer_surface;
-
-static const GLchar *vertex_shader_plain = " \
-#version 150 core\n \
-in vec2 input_vertex_coordinates; \
-void main() \
-{ \
- gl_Position = vec4(input_vertex_coordinates.x, input_vertex_coordinates.y, 0.0, 1.0); \
-} \
-";
-
-static const GLchar *vertex_shader_texture = " \
-#version 150 core\n \
-in vec2 input_vertex_coordinates; \
-in vec2 input_texture_coordinates; \
-out vec2 texture_coordinates; \
-void main() \
-{ \
- texture_coordinates = input_texture_coordinates; \
- gl_Position = vec4(input_vertex_coordinates.x, input_vertex_coordinates.y, 0.0, 1.0); \
-} \
-";
-
-static const GLchar *fragment_shader_texture = " \
-#version 150 core\n \
-uniform sampler2D tex; \
-in vec2 texture_coordinates; \
-out vec4 fragment; \
-void main() \
-{ \
- fragment = texture2D(tex, texture_coordinates); \
-} \
-";
-
-static const GLchar *fragment_shader_texture_colour_key = " \
-#version 150 core\n \
-uniform sampler2D tex; \
-in vec2 texture_coordinates; \
-out vec4 fragment; \
-void main() \
-{ \
- vec4 colour = texture2D(tex, texture_coordinates); \
-\
- if (colour.xyz == vec3(0.0f, 0.0f, 0.0f)) \
- discard; \
-\
- fragment = colour; \
-} \
-";
-
-static const GLchar *fragment_shader_colour_fill = " \
-#version 150 core\n \
-uniform vec4 colour; \
-out vec4 fragment; \
-void main() \
-{ \
- fragment = colour; \
-} \
-";
-
-static const GLchar *fragment_shader_glyph = " \
-#version 150 core\n \
-uniform sampler2D tex; \
-uniform vec4 colour; \
-in vec2 texture_coordinates; \
-out vec4 fragment; \
-void main() \
-{ \
- fragment = colour * vec4(1.0, 1.0, 1.0, texture2D(tex, texture_coordinates).r); \
-} \
-";
-
-static void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void* userParam)
-{
- if (type == GL_DEBUG_TYPE_ERROR)
- printf("GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
- ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
- type, severity, message);
-}
-
-static GLuint CompileShader(const char *vertex_shader_source, const char *fragment_shader_source)
-{
- GLint shader_status;
-
- GLuint program_id = glCreateProgram();
-
- GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
- glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
- glCompileShader(vertex_shader);
-
- glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &shader_status);
- if (shader_status != GL_TRUE)
- return 0;
-
- glAttachShader(program_id, vertex_shader);
-
- GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
- glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
- glCompileShader(fragment_shader);
-
- glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &shader_status);
- if (shader_status != GL_TRUE)
- return 0;
-
- glAttachShader(program_id, fragment_shader);
-
- glBindAttribLocation(program_id, 1, "input_vertex_coordinates");
- glBindAttribLocation(program_id, 2, "input_texture_coordinates");
-
- glLinkProgram(program_id);
-
- glGetProgramiv(program_id, GL_LINK_STATUS, &shader_status);
- if (shader_status != GL_TRUE)
- return 0;
-
- return program_id;
-}
-
-SDL_Window* Backend_CreateWindow(const char *title, int width, int height)
-{
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
-
- return SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL);
-}
-
-BOOL Backend_Init(SDL_Window *p_window)
-{
- window = p_window;
-
- int window_width, window_height;
- SDL_GetWindowSize(window, &window_width, &window_height);
-
- context = SDL_GL_CreateContext(window);
-
- if (glewInit() != GLEW_OK)
- return FALSE;
-
- // Check if the platform supports OpenGL 3.2
- if (!GLEW_VERSION_3_2)
- return FALSE;
-
- // During init, enable debug output
- glEnable(GL_DEBUG_OUTPUT);
- glDebugMessageCallback(MessageCallback, 0);
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT);
-
- // Set up VAO
- GLuint vertex_array_id;
- glGenVertexArrays(1, &vertex_array_id);
- glBindVertexArray(vertex_array_id);
-
- // Set up VBO
- glGenBuffers(1, &vertex_buffer_id);
- glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_buffer), NULL, GL_DYNAMIC_DRAW);
-
- // Set up IBO
- const GLuint indices[4] = {0, 1, 2, 3};
- GLuint indices_buffer_id;
- glGenBuffers(1, &indices_buffer_id);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices_buffer_id);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
-
- // Set up our shaders
- program_texture = CompileShader(vertex_shader_texture, fragment_shader_texture);
- program_texture_colour_key = CompileShader(vertex_shader_texture, fragment_shader_texture_colour_key);
- program_colour_fill = CompileShader(vertex_shader_plain, fragment_shader_colour_fill);
- program_glyph = CompileShader(vertex_shader_texture, fragment_shader_glyph);
-
- if (program_texture == 0 || program_texture_colour_key == 0 || program_colour_fill == 0 || program_glyph == 0)
- printf("Failed to compile shaders\n");
-
- // Get shader uniforms
- program_colour_fill_uniform_colour = glGetUniformLocation(program_colour_fill, "colour");
- program_glyph_uniform_colour = glGetUniformLocation(program_glyph, "colour");
-
- glEnableVertexAttribArray(1);
- glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
- glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(sizeof(GLfloat) * 4 * 2));
-
- // Set up framebuffer (used for surface-to-surface blitting)
- glGenFramebuffers(1, &framebuffer_id);
- glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
-
- // Set up framebuffer screen texture (used for screen-to-surface blitting)
- glGenTextures(1, &framebuffer_surface.texture_id);
- glBindTexture(GL_TEXTURE_2D, framebuffer_surface.texture_id);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, window_width, window_height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-
- framebuffer_surface.width = window_width;
- framebuffer_surface.height = window_height;
-
- return TRUE;
-}
-
-void Backend_Deinit(void)
-{
- glDeleteTextures(1, &framebuffer_surface.texture_id);
- glDeleteFramebuffers(1, &framebuffer_id);
- glDeleteProgram(program_glyph);
- glDeleteProgram(program_colour_fill);
- glDeleteProgram(program_texture_colour_key);
- glDeleteProgram(program_texture);
- SDL_GL_DeleteContext(context);
-}
-
-void Backend_DrawScreen(void)
-{
- glUseProgram(program_texture);
-
- // Enable texture coordinates, since this uses textures
- glEnableVertexAttribArray(2);
-
- // Target actual screen, and not our framebuffer
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
- glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
-
- // Draw framebuffer to screen
- glBindTexture(GL_TEXTURE_2D, framebuffer_surface.texture_id);
-
- vertex_buffer.texture_coordinates[0][0] = 0.0f;
- vertex_buffer.texture_coordinates[0][1] = 1.0f;
- vertex_buffer.texture_coordinates[1][0] = 1.0f;
- vertex_buffer.texture_coordinates[1][1] = 1.0f;
- vertex_buffer.texture_coordinates[2][0] = 1.0f;
- vertex_buffer.texture_coordinates[2][1] = 0.0f;
- vertex_buffer.texture_coordinates[3][0] = 0.0f;
- vertex_buffer.texture_coordinates[3][1] = 0.0f;
-
- vertex_buffer.vertexes[0][0] = -1.0f;
- vertex_buffer.vertexes[0][1] = -1.0f;
- vertex_buffer.vertexes[1][0] = 1.0f;
- vertex_buffer.vertexes[1][1] = -1.0f;
- vertex_buffer.vertexes[2][0] = 1.0f;
- vertex_buffer.vertexes[2][1] = 1.0f;
- vertex_buffer.vertexes[3][0] = -1.0f;
- vertex_buffer.vertexes[3][1] = 1.0f;
-
- glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
- glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
-
- SDL_GL_SwapWindow(window);
-
- // According to https://www.khronos.org/opengl/wiki/Common_Mistakes#Swap_Buffers
- // the buffer should always be cleared
- glClear(GL_COLOR_BUFFER_BIT);
-
- // Switch back to our framebuffer
- glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
-}
-
-Backend_Surface* Backend_CreateSurface(unsigned int width, unsigned int height)
-{
- Backend_Surface *surface = (Backend_Surface*)malloc(sizeof(Backend_Surface));
-
- if (surface == NULL)
- return NULL;
-
- glGenTextures(1, &surface->texture_id);
- glBindTexture(GL_TEXTURE_2D, surface->texture_id);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-
- surface->width = width;
- surface->height = height;
-
- return surface;
-}
-
-void Backend_FreeSurface(Backend_Surface *surface)
-{
- if (surface == NULL)
- return;
-
- glDeleteTextures(1, &surface->texture_id);
- free(surface);
-}
-
-unsigned char* Backend_Lock(Backend_Surface *surface, unsigned int *pitch)
-{
- if (surface == NULL)
- return NULL;
-
- surface->pixels = (unsigned char*)malloc(surface->width * surface->height * 3);
- *pitch = surface->width * 3;
- return surface->pixels;
-}
-
-void Backend_Unlock(Backend_Surface *surface)
-{
- if (surface == NULL)
- return;
-
- glBindTexture(GL_TEXTURE_2D, surface->texture_id);
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, surface->width, surface->height, GL_RGB, GL_UNSIGNED_BYTE, surface->pixels);
- free(surface->pixels);
-}
-
-static void BlitCommon(Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key)
-{
- if (rect->right - rect->left < 0 || rect->bottom - rect->top < 0)
- return;
-
- // Switch to colour-key shader if we have to
- glUseProgram(colour_key ? program_texture_colour_key : program_texture);
-
- // Enable texture coordinates, since this uses textures
- glEnableVertexAttribArray(2);
-
- glBindTexture(GL_TEXTURE_2D, source_surface->texture_id);
-
- const GLfloat texture_left = (GLfloat)rect->left / (GLfloat)source_surface->width;
- const GLfloat texture_right = (GLfloat)rect->right / (GLfloat)source_surface->width;
- const GLfloat texture_top = (GLfloat)rect->top / (GLfloat)source_surface->height;
- const GLfloat texture_bottom = (GLfloat)rect->bottom / (GLfloat)source_surface->height;
-
- const GLfloat vertex_left = (x * (2.0f / destination_surface->width)) - 1.0f;
- const GLfloat vertex_right = ((x + (rect->right - rect->left)) * (2.0f / destination_surface->width)) - 1.0f;
- const GLfloat vertex_top = (y * (2.0f / destination_surface->height)) - 1.0f;
- const GLfloat vertex_bottom = ((y + (rect->bottom - rect->top)) * (2.0f / destination_surface->height)) - 1.0f;
-
- vertex_buffer.texture_coordinates[0][0] = texture_left;
- vertex_buffer.texture_coordinates[0][1] = texture_top;
- vertex_buffer.texture_coordinates[1][0] = texture_right;
- vertex_buffer.texture_coordinates[1][1] = texture_top;
- vertex_buffer.texture_coordinates[2][0] = texture_right;
- vertex_buffer.texture_coordinates[2][1] = texture_bottom;
- vertex_buffer.texture_coordinates[3][0] = texture_left;
- vertex_buffer.texture_coordinates[3][1] = texture_bottom;
-
- vertex_buffer.vertexes[0][0] = vertex_left;
- vertex_buffer.vertexes[0][1] = vertex_top;
- vertex_buffer.vertexes[1][0] = vertex_right;
- vertex_buffer.vertexes[1][1] = vertex_top;
- vertex_buffer.vertexes[2][0] = vertex_right;
- vertex_buffer.vertexes[2][1] = vertex_bottom;
- vertex_buffer.vertexes[3][0] = vertex_left;
- vertex_buffer.vertexes[3][1] = vertex_bottom;
-
- glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
- glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
-}
-
-void Backend_Blit(Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key)
-{
- if (source_surface == NULL || destination_surface == NULL)
- return;
-
- // Point our framebuffer to the destination texture
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, destination_surface->texture_id, 0);
- glViewport(0, 0, destination_surface->width, destination_surface->height);
-
- BlitCommon(source_surface, rect, destination_surface, x, y, colour_key);
-}
-
-void Backend_BlitToScreen(Backend_Surface *source_surface, const RECT *rect, long x, long y, BOOL colour_key)
-{
- if (source_surface == NULL)
- return;
-
- // Point our framebuffer to the screen texture
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer_surface.texture_id, 0);
- glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
-
- BlitCommon(source_surface, rect, &framebuffer_surface, x, y, colour_key);
-}
-
-static void ColourFillCommon(Backend_Surface *surface, const RECT *rect, unsigned char red, unsigned char green, unsigned char blue)
-{
- if (rect->right - rect->left < 0 || rect->bottom - rect->top < 0)
- return;
-
- glUseProgram(program_colour_fill);
-
- // Disable texture coordinate array, since this doesn't use textures
- glDisableVertexAttribArray(2);
-
- glUniform4f(program_colour_fill_uniform_colour, red / 255.0f, green / 255.0f, blue / 255.0f, 1.0f);
-
- const GLfloat vertex_left = (rect->left * (2.0f / surface->width)) - 1.0f;
- const GLfloat vertex_right = (rect->right * (2.0f / surface->width)) - 1.0f;
- const GLfloat vertex_top = (rect->top * (2.0f / surface->height)) - 1.0f;
- const GLfloat vertex_bottom = (rect->bottom * (2.0f / surface->height)) - 1.0f;
-
- vertex_buffer.vertexes[0][0] = vertex_left;
- vertex_buffer.vertexes[0][1] = vertex_top;
- vertex_buffer.vertexes[1][0] = vertex_right;
- vertex_buffer.vertexes[1][1] = vertex_top;
- vertex_buffer.vertexes[2][0] = vertex_right;
- vertex_buffer.vertexes[2][1] = vertex_bottom;
- vertex_buffer.vertexes[3][0] = vertex_left;
- vertex_buffer.vertexes[3][1] = vertex_bottom;
-
- glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
- glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
-}
-
-void Backend_ColourFill(Backend_Surface *surface, const RECT *rect, unsigned char red, unsigned char green, unsigned char blue)
-{
- if (surface == NULL)
- return;
-
- // Point our framebuffer to the destination texture
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, surface->texture_id, 0);
- glViewport(0, 0, surface->width, surface->height);
-
- ColourFillCommon(surface, rect, red, green, blue);
-}
-
-void Backend_ColourFillToScreen(const RECT *rect, unsigned char red, unsigned char green, unsigned char blue)
-{
- // Point our framebuffer to the screen texture
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer_surface.texture_id, 0);
- glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
-
- ColourFillCommon(&framebuffer_surface, rect, red, green, blue);
-}
-
-void Backend_ScreenToSurface(Backend_Surface *surface, const RECT *rect)
-{
- if (surface == NULL)
- return;
-
- // Point our framebuffer to the destination texture
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, surface->texture_id, 0);
- glViewport(0, 0, surface->width, surface->height);
-
- BlitCommon(&framebuffer_surface, rect, surface, rect->left, rect->top, FALSE);
-}
-
-BOOL Backend_SupportsSubpixelGlyph(void)
-{
- return FALSE; // Per-component alpha is available as an extension, but I haven't looked into it yet
-}
-
-Backend_Glyph* Backend_LoadGlyph(const unsigned char *pixels, unsigned int width, unsigned int height, int pitch, unsigned short total_greys, unsigned char pixel_mode)
-{
- Backend_Glyph *glyph = (Backend_Glyph*)malloc(sizeof(Backend_Glyph));
-
- if (glyph == NULL)
- return NULL;
-
- const int destination_pitch = (width + 3) & ~3; // Round up to the nearest 4 (OpenGL needs this)
-
- unsigned char *buffer = (unsigned char*)malloc(destination_pitch * height);
-
- if (buffer == NULL)
- {
- free(glyph);
- return NULL;
- }
-
- switch (pixel_mode)
- {
- // FONT_PIXEL_MODE_LCD is unsupported
-
- case FONT_PIXEL_MODE_GRAY:
- for (unsigned int y = 0; y < height; ++y)
- {
- const unsigned char *source_pointer = pixels + y * pitch;
- unsigned char *destination_pointer = buffer + y * destination_pitch;
-
- for (unsigned int x = 0; x < width; ++x)
- {
- *destination_pointer++ = (unsigned char)(pow((double)*source_pointer++ / (total_greys - 1), 1.0 / 1.8) * 255.0);
- }
- }
-
- break;
-
- case FONT_PIXEL_MODE_MONO:
- for (unsigned int y = 0; y < height; ++y)
- {
- const unsigned char *source_pointer = pixels + y * pitch;
- unsigned char *destination_pointer = buffer + y * destination_pitch;
-
- for (unsigned int x = 0; x < width; ++x)
- {
- *destination_pointer++ = *source_pointer++ ? 0xFF : 0;
- }
- }
-
- break;
- }
-
- glGenTextures(1, &glyph->texture_id);
- glBindTexture(GL_TEXTURE_2D, glyph->texture_id);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, buffer);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-
- glyph->width = width;
- glyph->height = height;
-
- free(buffer);
-
- return glyph;
-}
-
-void Backend_UnloadGlyph(Backend_Glyph *glyph)
-{
- if (glyph == NULL)
- return;
-
- glDeleteTextures(1, &glyph->texture_id);
- free(glyph);
-}
-
-static void DrawGlyphCommon(Backend_Surface *surface, Backend_Glyph *glyph, long x, long y, const unsigned char *colours)
-{
- glUseProgram(program_glyph);
-
- // Enable texture coordinates, since this uses textures
- glEnableVertexAttribArray(2);
-
- glBindTexture(GL_TEXTURE_2D, glyph->texture_id);
-
- const GLfloat vertex_left = (x * (2.0f / surface->width)) - 1.0f;
- const GLfloat vertex_right = ((x + glyph->width) * (2.0f / surface->width)) - 1.0f;
- const GLfloat vertex_top = (y * (2.0f / surface->height)) - 1.0f;
- const GLfloat vertex_bottom = ((y + glyph->height) * (2.0f / surface->height)) - 1.0f;
-
- glUniform4f(program_glyph_uniform_colour, colours[0] / 255.0f, colours[1] / 255.0f, colours[2] / 255.0f, 1.0f);
-
- vertex_buffer.texture_coordinates[0][0] = 0.0f;
- vertex_buffer.texture_coordinates[0][1] = 0.0f;
- vertex_buffer.texture_coordinates[1][0] = 1.0f;
- vertex_buffer.texture_coordinates[1][1] = 0.0f;
- vertex_buffer.texture_coordinates[2][0] = 1.0f;
- vertex_buffer.texture_coordinates[2][1] = 1.0f;
- vertex_buffer.texture_coordinates[3][0] = 0.0f;
- vertex_buffer.texture_coordinates[3][1] = 1.0f;
-
- vertex_buffer.vertexes[0][0] = vertex_left;
- vertex_buffer.vertexes[0][1] = vertex_top;
- vertex_buffer.vertexes[1][0] = vertex_right;
- vertex_buffer.vertexes[1][1] = vertex_top;
- vertex_buffer.vertexes[2][0] = vertex_right;
- vertex_buffer.vertexes[2][1] = vertex_bottom;
- vertex_buffer.vertexes[3][0] = vertex_left;
- vertex_buffer.vertexes[3][1] = vertex_bottom;
-
- glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
- glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
-}
-
-void Backend_DrawGlyph(Backend_Surface *surface, Backend_Glyph *glyph, long x, long y, const unsigned char *colours)
-{
- if (glyph == NULL || surface == NULL)
- return;
-
- // Point our framebuffer to the destination texture
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, surface->texture_id, 0);
- glViewport(0, 0, surface->width, surface->height);
-
- DrawGlyphCommon(surface, glyph, x, y, colours);
-}
-
-void Backend_DrawGlyphToScreen(Backend_Glyph *glyph, long x, long y, const unsigned char *colours)
-{
- if (glyph == NULL)
- return;
-
- // Point our framebuffer to the screen texture
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer_surface.texture_id, 0);
- glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
-
- DrawGlyphCommon(&framebuffer_surface, glyph, x, y, colours);
-}
-
-void Backend_HandleDeviceLoss(void)
-{
- // No problem for us
-}
-
-void Backend_HandleWindowResize(void)
-{
- // No problem for us
-}
--- /dev/null
+++ b/src/Backends/Rendering/OpenGL3.cpp
@@ -1,0 +1,637 @@
+#include "../Rendering.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <GL/glew.h>
+#include "SDL.h"
+
+#include "../../WindowsWrapper.h"
+
+#include "../../Font.h"
+
+typedef struct Backend_Surface
+{
+ GLuint texture_id;
+ unsigned int width;
+ unsigned int height;
+ unsigned char *pixels;
+} Backend_Surface;
+
+typedef struct Backend_Glyph
+{
+ GLuint texture_id;
+ unsigned int width;
+ unsigned int height;
+} Backend_Glyph;
+
+static SDL_Window *window;
+static SDL_GLContext context;
+
+static GLuint program_texture;
+static GLuint program_texture_colour_key;
+static GLuint program_colour_fill;
+static GLuint program_glyph;
+
+static GLint program_colour_fill_uniform_colour;
+static GLint program_glyph_uniform_colour;
+
+static GLuint framebuffer_id;
+static GLuint vertex_buffer_id;
+
+static struct
+{
+ GLfloat vertexes[4][2];
+ GLfloat texture_coordinates[4][2];
+} vertex_buffer;
+
+static Backend_Surface framebuffer_surface;
+
+static const GLchar *vertex_shader_plain = " \
+#version 150 core\n \
+in vec2 input_vertex_coordinates; \
+void main() \
+{ \
+ gl_Position = vec4(input_vertex_coordinates.x, input_vertex_coordinates.y, 0.0, 1.0); \
+} \
+";
+
+static const GLchar *vertex_shader_texture = " \
+#version 150 core\n \
+in vec2 input_vertex_coordinates; \
+in vec2 input_texture_coordinates; \
+out vec2 texture_coordinates; \
+void main() \
+{ \
+ texture_coordinates = input_texture_coordinates; \
+ gl_Position = vec4(input_vertex_coordinates.x, input_vertex_coordinates.y, 0.0, 1.0); \
+} \
+";
+
+static const GLchar *fragment_shader_texture = " \
+#version 150 core\n \
+uniform sampler2D tex; \
+in vec2 texture_coordinates; \
+out vec4 fragment; \
+void main() \
+{ \
+ fragment = texture2D(tex, texture_coordinates); \
+} \
+";
+
+static const GLchar *fragment_shader_texture_colour_key = " \
+#version 150 core\n \
+uniform sampler2D tex; \
+in vec2 texture_coordinates; \
+out vec4 fragment; \
+void main() \
+{ \
+ vec4 colour = texture2D(tex, texture_coordinates); \
+\
+ if (colour.xyz == vec3(0.0f, 0.0f, 0.0f)) \
+ discard; \
+\
+ fragment = colour; \
+} \
+";
+
+static const GLchar *fragment_shader_colour_fill = " \
+#version 150 core\n \
+uniform vec4 colour; \
+out vec4 fragment; \
+void main() \
+{ \
+ fragment = colour; \
+} \
+";
+
+static const GLchar *fragment_shader_glyph = " \
+#version 150 core\n \
+uniform sampler2D tex; \
+uniform vec4 colour; \
+in vec2 texture_coordinates; \
+out vec4 fragment; \
+void main() \
+{ \
+ fragment = colour * vec4(1.0, 1.0, 1.0, texture2D(tex, texture_coordinates).r); \
+} \
+";
+
+static void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void* userParam)
+{
+ if (type == GL_DEBUG_TYPE_ERROR)
+ printf("GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+ ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
+ type, severity, message);
+}
+
+static GLuint CompileShader(const char *vertex_shader_source, const char *fragment_shader_source)
+{
+ GLint shader_status;
+
+ GLuint program_id = glCreateProgram();
+
+ GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
+ glCompileShader(vertex_shader);
+
+ glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &shader_status);
+ if (shader_status != GL_TRUE)
+ return 0;
+
+ glAttachShader(program_id, vertex_shader);
+
+ GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
+ glCompileShader(fragment_shader);
+
+ glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &shader_status);
+ if (shader_status != GL_TRUE)
+ return 0;
+
+ glAttachShader(program_id, fragment_shader);
+
+ glBindAttribLocation(program_id, 1, "input_vertex_coordinates");
+ glBindAttribLocation(program_id, 2, "input_texture_coordinates");
+
+ glLinkProgram(program_id);
+
+ glGetProgramiv(program_id, GL_LINK_STATUS, &shader_status);
+ if (shader_status != GL_TRUE)
+ return 0;
+
+ return program_id;
+}
+
+SDL_Window* Backend_CreateWindow(const char *title, int width, int height)
+{
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
+
+ return SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL);
+}
+
+BOOL Backend_Init(SDL_Window *p_window)
+{
+ window = p_window;
+
+ int window_width, window_height;
+ SDL_GetWindowSize(window, &window_width, &window_height);
+
+ context = SDL_GL_CreateContext(window);
+
+ if (glewInit() != GLEW_OK)
+ return FALSE;
+
+ // Check if the platform supports OpenGL 3.2
+ if (!GLEW_VERSION_3_2)
+ return FALSE;
+
+ // During init, enable debug output
+ glEnable(GL_DEBUG_OUTPUT);
+ glDebugMessageCallback(MessageCallback, 0);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // Set up VAO
+ GLuint vertex_array_id;
+ glGenVertexArrays(1, &vertex_array_id);
+ glBindVertexArray(vertex_array_id);
+
+ // Set up VBO
+ glGenBuffers(1, &vertex_buffer_id);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_buffer), NULL, GL_DYNAMIC_DRAW);
+
+ // Set up IBO
+ const GLuint indices[4] = {0, 1, 2, 3};
+ GLuint indices_buffer_id;
+ glGenBuffers(1, &indices_buffer_id);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices_buffer_id);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
+
+ // Set up our shaders
+ program_texture = CompileShader(vertex_shader_texture, fragment_shader_texture);
+ program_texture_colour_key = CompileShader(vertex_shader_texture, fragment_shader_texture_colour_key);
+ program_colour_fill = CompileShader(vertex_shader_plain, fragment_shader_colour_fill);
+ program_glyph = CompileShader(vertex_shader_texture, fragment_shader_glyph);
+
+ if (program_texture == 0 || program_texture_colour_key == 0 || program_colour_fill == 0 || program_glyph == 0)
+ printf("Failed to compile shaders\n");
+
+ // Get shader uniforms
+ program_colour_fill_uniform_colour = glGetUniformLocation(program_colour_fill, "colour");
+ program_glyph_uniform_colour = glGetUniformLocation(program_glyph, "colour");
+
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
+ glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(sizeof(GLfloat) * 4 * 2));
+
+ // Set up framebuffer (used for surface-to-surface blitting)
+ glGenFramebuffers(1, &framebuffer_id);
+ glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
+
+ // Set up framebuffer screen texture (used for screen-to-surface blitting)
+ glGenTextures(1, &framebuffer_surface.texture_id);
+ glBindTexture(GL_TEXTURE_2D, framebuffer_surface.texture_id);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, window_width, window_height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ framebuffer_surface.width = window_width;
+ framebuffer_surface.height = window_height;
+
+ return TRUE;
+}
+
+void Backend_Deinit(void)
+{
+ glDeleteTextures(1, &framebuffer_surface.texture_id);
+ glDeleteFramebuffers(1, &framebuffer_id);
+ glDeleteProgram(program_glyph);
+ glDeleteProgram(program_colour_fill);
+ glDeleteProgram(program_texture_colour_key);
+ glDeleteProgram(program_texture);
+ SDL_GL_DeleteContext(context);
+}
+
+void Backend_DrawScreen(void)
+{
+ glUseProgram(program_texture);
+
+ // Enable texture coordinates, since this uses textures
+ glEnableVertexAttribArray(2);
+
+ // Target actual screen, and not our framebuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
+
+ // Draw framebuffer to screen
+ glBindTexture(GL_TEXTURE_2D, framebuffer_surface.texture_id);
+
+ vertex_buffer.texture_coordinates[0][0] = 0.0f;
+ vertex_buffer.texture_coordinates[0][1] = 1.0f;
+ vertex_buffer.texture_coordinates[1][0] = 1.0f;
+ vertex_buffer.texture_coordinates[1][1] = 1.0f;
+ vertex_buffer.texture_coordinates[2][0] = 1.0f;
+ vertex_buffer.texture_coordinates[2][1] = 0.0f;
+ vertex_buffer.texture_coordinates[3][0] = 0.0f;
+ vertex_buffer.texture_coordinates[3][1] = 0.0f;
+
+ vertex_buffer.vertexes[0][0] = -1.0f;
+ vertex_buffer.vertexes[0][1] = -1.0f;
+ vertex_buffer.vertexes[1][0] = 1.0f;
+ vertex_buffer.vertexes[1][1] = -1.0f;
+ vertex_buffer.vertexes[2][0] = 1.0f;
+ vertex_buffer.vertexes[2][1] = 1.0f;
+ vertex_buffer.vertexes[3][0] = -1.0f;
+ vertex_buffer.vertexes[3][1] = 1.0f;
+
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
+ glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
+
+ SDL_GL_SwapWindow(window);
+
+ // According to https://www.khronos.org/opengl/wiki/Common_Mistakes#Swap_Buffers
+ // the buffer should always be cleared
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // Switch back to our framebuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
+}
+
+Backend_Surface* Backend_CreateSurface(unsigned int width, unsigned int height)
+{
+ Backend_Surface *surface = (Backend_Surface*)malloc(sizeof(Backend_Surface));
+
+ if (surface == NULL)
+ return NULL;
+
+ glGenTextures(1, &surface->texture_id);
+ glBindTexture(GL_TEXTURE_2D, surface->texture_id);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ surface->width = width;
+ surface->height = height;
+
+ return surface;
+}
+
+void Backend_FreeSurface(Backend_Surface *surface)
+{
+ if (surface == NULL)
+ return;
+
+ glDeleteTextures(1, &surface->texture_id);
+ free(surface);
+}
+
+unsigned char* Backend_Lock(Backend_Surface *surface, unsigned int *pitch)
+{
+ if (surface == NULL)
+ return NULL;
+
+ surface->pixels = (unsigned char*)malloc(surface->width * surface->height * 3);
+ *pitch = surface->width * 3;
+ return surface->pixels;
+}
+
+void Backend_Unlock(Backend_Surface *surface)
+{
+ if (surface == NULL)
+ return;
+
+ glBindTexture(GL_TEXTURE_2D, surface->texture_id);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, surface->width, surface->height, GL_RGB, GL_UNSIGNED_BYTE, surface->pixels);
+ free(surface->pixels);
+}
+
+static void BlitCommon(Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key)
+{
+ if (rect->right - rect->left < 0 || rect->bottom - rect->top < 0)
+ return;
+
+ // Switch to colour-key shader if we have to
+ glUseProgram(colour_key ? program_texture_colour_key : program_texture);
+
+ // Enable texture coordinates, since this uses textures
+ glEnableVertexAttribArray(2);
+
+ glBindTexture(GL_TEXTURE_2D, source_surface->texture_id);
+
+ const GLfloat texture_left = (GLfloat)rect->left / (GLfloat)source_surface->width;
+ const GLfloat texture_right = (GLfloat)rect->right / (GLfloat)source_surface->width;
+ const GLfloat texture_top = (GLfloat)rect->top / (GLfloat)source_surface->height;
+ const GLfloat texture_bottom = (GLfloat)rect->bottom / (GLfloat)source_surface->height;
+
+ const GLfloat vertex_left = (x * (2.0f / destination_surface->width)) - 1.0f;
+ const GLfloat vertex_right = ((x + (rect->right - rect->left)) * (2.0f / destination_surface->width)) - 1.0f;
+ const GLfloat vertex_top = (y * (2.0f / destination_surface->height)) - 1.0f;
+ const GLfloat vertex_bottom = ((y + (rect->bottom - rect->top)) * (2.0f / destination_surface->height)) - 1.0f;
+
+ vertex_buffer.texture_coordinates[0][0] = texture_left;
+ vertex_buffer.texture_coordinates[0][1] = texture_top;
+ vertex_buffer.texture_coordinates[1][0] = texture_right;
+ vertex_buffer.texture_coordinates[1][1] = texture_top;
+ vertex_buffer.texture_coordinates[2][0] = texture_right;
+ vertex_buffer.texture_coordinates[2][1] = texture_bottom;
+ vertex_buffer.texture_coordinates[3][0] = texture_left;
+ vertex_buffer.texture_coordinates[3][1] = texture_bottom;
+
+ vertex_buffer.vertexes[0][0] = vertex_left;
+ vertex_buffer.vertexes[0][1] = vertex_top;
+ vertex_buffer.vertexes[1][0] = vertex_right;
+ vertex_buffer.vertexes[1][1] = vertex_top;
+ vertex_buffer.vertexes[2][0] = vertex_right;
+ vertex_buffer.vertexes[2][1] = vertex_bottom;
+ vertex_buffer.vertexes[3][0] = vertex_left;
+ vertex_buffer.vertexes[3][1] = vertex_bottom;
+
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
+ glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
+}
+
+void Backend_Blit(Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key)
+{
+ if (source_surface == NULL || destination_surface == NULL)
+ return;
+
+ // Point our framebuffer to the destination texture
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, destination_surface->texture_id, 0);
+ glViewport(0, 0, destination_surface->width, destination_surface->height);
+
+ BlitCommon(source_surface, rect, destination_surface, x, y, colour_key);
+}
+
+void Backend_BlitToScreen(Backend_Surface *source_surface, const RECT *rect, long x, long y, BOOL colour_key)
+{
+ if (source_surface == NULL)
+ return;
+
+ // Point our framebuffer to the screen texture
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer_surface.texture_id, 0);
+ glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
+
+ BlitCommon(source_surface, rect, &framebuffer_surface, x, y, colour_key);
+}
+
+static void ColourFillCommon(Backend_Surface *surface, const RECT *rect, unsigned char red, unsigned char green, unsigned char blue)
+{
+ if (rect->right - rect->left < 0 || rect->bottom - rect->top < 0)
+ return;
+
+ glUseProgram(program_colour_fill);
+
+ // Disable texture coordinate array, since this doesn't use textures
+ glDisableVertexAttribArray(2);
+
+ glUniform4f(program_colour_fill_uniform_colour, red / 255.0f, green / 255.0f, blue / 255.0f, 1.0f);
+
+ const GLfloat vertex_left = (rect->left * (2.0f / surface->width)) - 1.0f;
+ const GLfloat vertex_right = (rect->right * (2.0f / surface->width)) - 1.0f;
+ const GLfloat vertex_top = (rect->top * (2.0f / surface->height)) - 1.0f;
+ const GLfloat vertex_bottom = (rect->bottom * (2.0f / surface->height)) - 1.0f;
+
+ vertex_buffer.vertexes[0][0] = vertex_left;
+ vertex_buffer.vertexes[0][1] = vertex_top;
+ vertex_buffer.vertexes[1][0] = vertex_right;
+ vertex_buffer.vertexes[1][1] = vertex_top;
+ vertex_buffer.vertexes[2][0] = vertex_right;
+ vertex_buffer.vertexes[2][1] = vertex_bottom;
+ vertex_buffer.vertexes[3][0] = vertex_left;
+ vertex_buffer.vertexes[3][1] = vertex_bottom;
+
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
+ glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
+}
+
+void Backend_ColourFill(Backend_Surface *surface, const RECT *rect, unsigned char red, unsigned char green, unsigned char blue)
+{
+ if (surface == NULL)
+ return;
+
+ // Point our framebuffer to the destination texture
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, surface->texture_id, 0);
+ glViewport(0, 0, surface->width, surface->height);
+
+ ColourFillCommon(surface, rect, red, green, blue);
+}
+
+void Backend_ColourFillToScreen(const RECT *rect, unsigned char red, unsigned char green, unsigned char blue)
+{
+ // Point our framebuffer to the screen texture
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer_surface.texture_id, 0);
+ glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
+
+ ColourFillCommon(&framebuffer_surface, rect, red, green, blue);
+}
+
+void Backend_ScreenToSurface(Backend_Surface *surface, const RECT *rect)
+{
+ if (surface == NULL)
+ return;
+
+ // Point our framebuffer to the destination texture
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, surface->texture_id, 0);
+ glViewport(0, 0, surface->width, surface->height);
+
+ BlitCommon(&framebuffer_surface, rect, surface, rect->left, rect->top, FALSE);
+}
+
+BOOL Backend_SupportsSubpixelGlyph(void)
+{
+ return FALSE; // Per-component alpha is available as an extension, but I haven't looked into it yet
+}
+
+Backend_Glyph* Backend_LoadGlyph(const unsigned char *pixels, unsigned int width, unsigned int height, int pitch, unsigned short total_greys, unsigned char pixel_mode)
+{
+ Backend_Glyph *glyph = (Backend_Glyph*)malloc(sizeof(Backend_Glyph));
+
+ if (glyph == NULL)
+ return NULL;
+
+ const int destination_pitch = (width + 3) & ~3; // Round up to the nearest 4 (OpenGL needs this)
+
+ unsigned char *buffer = (unsigned char*)malloc(destination_pitch * height);
+
+ if (buffer == NULL)
+ {
+ free(glyph);
+ return NULL;
+ }
+
+ switch (pixel_mode)
+ {
+ // FONT_PIXEL_MODE_LCD is unsupported
+
+ case FONT_PIXEL_MODE_GRAY:
+ for (unsigned int y = 0; y < height; ++y)
+ {
+ const unsigned char *source_pointer = pixels + y * pitch;
+ unsigned char *destination_pointer = buffer + y * destination_pitch;
+
+ for (unsigned int x = 0; x < width; ++x)
+ {
+ *destination_pointer++ = (unsigned char)(pow((double)*source_pointer++ / (total_greys - 1), 1.0 / 1.8) * 255.0);
+ }
+ }
+
+ break;
+
+ case FONT_PIXEL_MODE_MONO:
+ for (unsigned int y = 0; y < height; ++y)
+ {
+ const unsigned char *source_pointer = pixels + y * pitch;
+ unsigned char *destination_pointer = buffer + y * destination_pitch;
+
+ for (unsigned int x = 0; x < width; ++x)
+ {
+ *destination_pointer++ = *source_pointer++ ? 0xFF : 0;
+ }
+ }
+
+ break;
+ }
+
+ glGenTextures(1, &glyph->texture_id);
+ glBindTexture(GL_TEXTURE_2D, glyph->texture_id);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, buffer);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ glyph->width = width;
+ glyph->height = height;
+
+ free(buffer);
+
+ return glyph;
+}
+
+void Backend_UnloadGlyph(Backend_Glyph *glyph)
+{
+ if (glyph == NULL)
+ return;
+
+ glDeleteTextures(1, &glyph->texture_id);
+ free(glyph);
+}
+
+static void DrawGlyphCommon(Backend_Surface *surface, Backend_Glyph *glyph, long x, long y, const unsigned char *colours)
+{
+ glUseProgram(program_glyph);
+
+ // Enable texture coordinates, since this uses textures
+ glEnableVertexAttribArray(2);
+
+ glBindTexture(GL_TEXTURE_2D, glyph->texture_id);
+
+ const GLfloat vertex_left = (x * (2.0f / surface->width)) - 1.0f;
+ const GLfloat vertex_right = ((x + glyph->width) * (2.0f / surface->width)) - 1.0f;
+ const GLfloat vertex_top = (y * (2.0f / surface->height)) - 1.0f;
+ const GLfloat vertex_bottom = ((y + glyph->height) * (2.0f / surface->height)) - 1.0f;
+
+ glUniform4f(program_glyph_uniform_colour, colours[0] / 255.0f, colours[1] / 255.0f, colours[2] / 255.0f, 1.0f);
+
+ vertex_buffer.texture_coordinates[0][0] = 0.0f;
+ vertex_buffer.texture_coordinates[0][1] = 0.0f;
+ vertex_buffer.texture_coordinates[1][0] = 1.0f;
+ vertex_buffer.texture_coordinates[1][1] = 0.0f;
+ vertex_buffer.texture_coordinates[2][0] = 1.0f;
+ vertex_buffer.texture_coordinates[2][1] = 1.0f;
+ vertex_buffer.texture_coordinates[3][0] = 0.0f;
+ vertex_buffer.texture_coordinates[3][1] = 1.0f;
+
+ vertex_buffer.vertexes[0][0] = vertex_left;
+ vertex_buffer.vertexes[0][1] = vertex_top;
+ vertex_buffer.vertexes[1][0] = vertex_right;
+ vertex_buffer.vertexes[1][1] = vertex_top;
+ vertex_buffer.vertexes[2][0] = vertex_right;
+ vertex_buffer.vertexes[2][1] = vertex_bottom;
+ vertex_buffer.vertexes[3][0] = vertex_left;
+ vertex_buffer.vertexes[3][1] = vertex_bottom;
+
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_buffer), &vertex_buffer);
+ glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL);
+}
+
+void Backend_DrawGlyph(Backend_Surface *surface, Backend_Glyph *glyph, long x, long y, const unsigned char *colours)
+{
+ if (glyph == NULL || surface == NULL)
+ return;
+
+ // Point our framebuffer to the destination texture
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, surface->texture_id, 0);
+ glViewport(0, 0, surface->width, surface->height);
+
+ DrawGlyphCommon(surface, glyph, x, y, colours);
+}
+
+void Backend_DrawGlyphToScreen(Backend_Glyph *glyph, long x, long y, const unsigned char *colours)
+{
+ if (glyph == NULL)
+ return;
+
+ // Point our framebuffer to the screen texture
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer_surface.texture_id, 0);
+ glViewport(0, 0, framebuffer_surface.width, framebuffer_surface.height);
+
+ DrawGlyphCommon(&framebuffer_surface, glyph, x, y, colours);
+}
+
+void Backend_HandleDeviceLoss(void)
+{
+ // No problem for us
+}
+
+void Backend_HandleWindowResize(void)
+{
+ // No problem for us
+}