From 05f382961dccc2979d3f7c675a6603d3043aa31f Mon Sep 17 00:00:00 2001 From: Clownacy Date: Mon, 22 Jul 2019 21:59:52 +0000 Subject: [PATCH] Added OpenGL 2.1 renderer Yay 100% hardware-acceleration. Yes, I know 2.1 is outdated and crappy, but it was the easiest one to write. I'll probably make an OpenGL 3.0 Core renderer at some point. Anyway, font rendering isn't here yet, because I plan to overhaul it. --- CMakeLists.txt | 9 +- src/Backends/Rendering/OpenGL2.cpp | 322 +++++++++++++++++++++++++++++ src/Main.cpp | 6 +- 3 files changed, 335 insertions(+), 2 deletions(-) create mode 100644 src/Backends/Rendering/OpenGL2.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 337312eb..1cb934a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -246,7 +246,9 @@ if(NONPORTABLE) target_compile_definitions(CSE2 PRIVATE NONPORTABLE) endif() -if(RENDERER MATCHES "Texture") +if(RENDERER MATCHES "OpenGL2") + target_sources(CSE2 PRIVATE "src/Backends/Rendering/OpenGL2.cpp") +elseif(RENDERER MATCHES "Texture") target_sources(CSE2 PRIVATE "src/Backends/Rendering/SDLTexture.cpp") elseif(RENDERER MATCHES "Surface") target_sources(CSE2 PRIVATE "src/Backends/Rendering/SDLSurface.cpp") @@ -402,6 +404,11 @@ else() target_link_libraries(CSE2 freetype) endif() +find_package(GLEW REQUIRED) +target_link_libraries(CSE2 GLEW::GLEW) +find_package(OpenGL REQUIRED) +target_link_libraries(CSE2 OpenGL::GL OpenGL::GLU) + ## # DoConfig diff --git a/src/Backends/Rendering/OpenGL2.cpp b/src/Backends/Rendering/OpenGL2.cpp new file mode 100644 index 00000000..c77688be --- /dev/null +++ b/src/Backends/Rendering/OpenGL2.cpp @@ -0,0 +1,322 @@ +#include "../Rendering.h" + +#include +#include + +#include +#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; + +static SDL_Window *window; +static SDL_GLContext context; +static GLuint colour_key_program_id; +static GLuint framebuffer_id; + +static Backend_Surface framebuffer_surface; + +static const GLchar *fragment_shader_source = " \ +#version 120\n \ +uniform sampler2D tex; \ +void main() \ +{ \ + vec4 colour = gl_Color * texture2D(tex, gl_TexCoord[0].st); \ +\ + if (colour.xyz == vec3(0.0f, 0.0f, 0.0f)) \ + discard; \ +\ + gl_FragColor = colour; \ +} \ +"; + +static GLuint CompileShader(const char *fragment_shader_source) +{ + GLint shader_status; + + GLuint program_id = glCreateProgram(); + + 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); + + glLinkProgram(program_id); + + glGetProgramiv(program_id, GL_LINK_STATUS, &shader_status); + if (shader_status != GL_TRUE) + return 0; + + return program_id; +} + +BOOL Backend_Init(SDL_Window *p_window) +{ + window = p_window; + + int screen_width, screen_height; + SDL_GetWindowSize(window, &screen_width, &screen_height); + + context = SDL_GL_CreateContext(window); + + if (glewInit() != GLEW_OK) + return FALSE; + + // Check for framebuffer object extension (is part of the core spec in OpenGL 3.0, but not 2.1) + if (!GLEW_EXT_framebuffer_object) + return FALSE; + +// glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glOrtho(0.0, screen_width, 0.0, screen_height, 1.0, -1.0); + +// glMatrixMode(GL_PROJECTION); +// glLoadIdentity(); + + glEnable(GL_TEXTURE_2D); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + // Set up blank default texture (it seems to be black by default, which sucks for colour modulation) + const unsigned char white_pixel[3] = {0xFF, 0xFF, 0xFF}; + glBindTexture(GL_TEXTURE_2D, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, white_pixel); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + // Set up our colour-key-enabled fragment shader + colour_key_program_id = CompileShader(fragment_shader_source); + + // Set up framebuffer (used for surface-to-surface blitting) + glGenFramebuffersEXT(1, &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, screen_width, screen_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 = screen_width; + framebuffer_surface.height = screen_height; + + return TRUE; +} + +void Backend_Deinit(void) +{ + glDeleteTextures(1, &framebuffer_surface.texture_id); + glDeleteFramebuffersEXT(1, &framebuffer_id); + glDeleteProgram(colour_key_program_id); + SDL_GL_DeleteContext(context); +} + +void Backend_DrawScreen(void) +{ + // Disable colour-keying + glUseProgram(0); + + // Target actual screen, and not our framebuffer + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + + // Draw framebuffer to screen + glPushMatrix(); + glLoadIdentity(); + + glBindTexture(GL_TEXTURE_2D, framebuffer_surface.texture_id); + + glBegin(GL_QUADS); + glColor3f(1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 1.0f); + glVertex2f(-1.0f, -1.0f); + glTexCoord2f(1.0f, 1.0f); + glVertex2f(1.0f, -1.0f); + glTexCoord2f(1.0f, 0.0f); + glVertex2f(1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); + glVertex2f(-1.0f, 1.0f); + glEnd(); + + glPopMatrix(); + + 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 + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 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) +{ + glDeleteTextures(1, &surface->texture_id); + free(surface); +} + +unsigned char* Backend_Lock(Backend_Surface *surface, unsigned int *pitch) +{ + surface->pixels = (unsigned char*)malloc(surface->width * surface->height * 3); + *pitch = surface->width * 3; + return surface->pixels; +} + +void Backend_Unlock(Backend_Surface *surface) +{ + 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, long x, long y, BOOL colour_key) +{ + // Switch to colour-key shader if we have to + glUseProgram(colour_key ? colour_key_program_id : 0); + + glBindTexture(GL_TEXTURE_2D, source_surface->texture_id); + + glBegin(GL_QUADS); + glColor3f(1.0f, 1.0f, 1.0f); + glTexCoord2f((GLfloat)rect->left / (GLfloat)source_surface->width, (GLfloat)rect->top / (GLfloat)source_surface->height); + glVertex2f((GLfloat)x, (GLfloat)y); + glTexCoord2f((GLfloat)rect->right / (GLfloat)source_surface->width, (GLfloat)rect->top / (GLfloat)source_surface->height); + glVertex2f((GLfloat)x + (rect->right - rect->left), (GLfloat)y); + glTexCoord2f((GLfloat)rect->right / (GLfloat)source_surface->width, (GLfloat)rect->bottom / (GLfloat)source_surface->height); + glVertex2f((GLfloat)x + (rect->right - rect->left), (GLfloat)y + (rect->bottom - rect->top)); + glTexCoord2f((GLfloat)rect->left / (GLfloat)source_surface->width, (GLfloat)rect->bottom / (GLfloat)source_surface->height); + glVertex2f((GLfloat)x, (GLfloat)y + (rect->bottom - rect->top)); + glEnd(); +} + +void Backend_Blit(Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key) +{ + // Point our framebuffer to the destination texture + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, destination_surface->texture_id, 0); + + // The matrix needs resetting to fit the dimensions of the destination texture + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.0, destination_surface->width, 0.0, destination_surface->height, 1.0, -1.0); + + BlitCommon(source_surface, rect, x, y, colour_key); + + glPopMatrix(); +} + +void Backend_BlitToScreen(Backend_Surface *source_surface, const RECT *rect, long x, long y, BOOL colour_key) +{ + // Point our framebuffer to the screen texture + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, framebuffer_surface.texture_id, 0); + + BlitCommon(source_surface, rect, x, y, colour_key); +} + +static void ColourFillCommon(const RECT *rect, unsigned char red, unsigned char green, unsigned char blue) +{ + // Disable colour-keying + glUseProgram(0); + + // Use blank default texture, for a solid colour-fill + glBindTexture(GL_TEXTURE_2D, 0); + + glBegin(GL_QUADS); + glColor3f(red / 255.0f, green / 255.0f, blue / 255.0f); + glVertex2f((GLfloat)rect->left, (GLfloat)rect->top); + glVertex2f((GLfloat)rect->right, (GLfloat)rect->top); + glVertex2f((GLfloat)rect->right, (GLfloat)rect->bottom); + glVertex2f((GLfloat)rect->left, (GLfloat)rect->bottom); + glEnd(); +} + +void Backend_ColourFill(Backend_Surface *surface, const RECT *rect, unsigned char red, unsigned char green, unsigned char blue) +{ + // Point our framebuffer to the destination texture + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, surface->texture_id, 0); + + // The matrix needs resetting to fit the dimensions of the destination texture + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.0, surface->width, 0.0, surface->height, 1.0, -1.0); + + ColourFillCommon(rect, red, green, blue); + + glPopMatrix(); +} + +void Backend_ColourFillToScreen(const RECT *rect, unsigned char red, unsigned char green, unsigned char blue) +{ + // Point our framebuffer to the screen texture + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, framebuffer_surface.texture_id, 0); + + ColourFillCommon(rect, red, green, blue); +} + +void Backend_ScreenToSurface(Backend_Surface *surface, const RECT *rect) +{ + // Point our framebuffer to the destination texture + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, surface->texture_id, 0); + + // The matrix needs resetting to fit the dimensions of the destination texture + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.0, surface->width, 0.0, surface->height, 1.0, -1.0); + + BlitCommon(&framebuffer_surface, rect, rect->left, rect->top, FALSE); + + glPopMatrix(); +} + +void Backend_DrawText(Backend_Surface *surface, FontObject *font, int x, int y, const char *text, unsigned long colour) +{ + // Soon +} + +void Backend_DrawTextToScreen(FontObject *font, int x, int y, const char *text, unsigned long colour) +{ + // Soon +} + +void Backend_HandleDeviceLoss(void) +{ + // No problem for us +} + +void Backend_HandleWindowResize(void) +{ + // No problem for us +} diff --git a/src/Main.cpp b/src/Main.cpp index b785164f..9d8fb8f4 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -256,8 +256,12 @@ int main(int argc, char *argv[]) windowHeight = WINDOW_HEIGHT * 2; } + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + // Create window - gWindow = SDL_CreateWindow(lpWindowName, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowWidth, windowHeight, 0); + gWindow = SDL_CreateWindow(lpWindowName, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowWidth, windowHeight, SDL_WINDOW_OPENGL); if (gWindow) {