ref: bf30ca05480103c118ef4503f2884cff9334e6dc
dir: /opengl.c/
#include "third_party/gl_core/gl_core_3_1.h"
#include <SDL.h>
#include <stdio.h>
#include <stdbool.h>
#include "types.h"
#include "util.h"
#include "glsl_shader.h"
#include "config.h"
#define CODE(...) #__VA_ARGS__
static SDL_Window *g_window;
static uint8 *g_screen_buffer;
static size_t g_screen_buffer_size;
static int g_draw_width, g_draw_height;
static unsigned int g_program, g_VAO;
static GlTextureWithSize g_texture;
static GlslShader *g_glsl_shader;
static void GL_APIENTRY MessageCallback(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar *message,
const void *userParam) {
if (type == GL_DEBUG_TYPE_OTHER)
return;
fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
type, severity, message);
if (type == GL_DEBUG_TYPE_ERROR)
Die("OpenGL error!\n");
}
static bool OpenGLRenderer_Init(SDL_Window *window) {
g_window = window;
SDL_GLContext context = SDL_GL_CreateContext(window);
const char *error = SDL_GetError();
SDL_GL_SetSwapInterval(1);
ogl_LoadFunctions();
if (!ogl_IsVersionGEQ(3, 3))
Die("You need OpenGL 3.3");
if (kDebugFlag) {
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(MessageCallback, 0);
}
glGenTextures(1, &g_texture.gl_texture);
static const float kVertices[] = {
// positions // texture coords
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f, // top left
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f, // bottom left
1.0f, 1.0f, 0.0f, 1.0f, 0.0f, // top right
1.0f, -1.0f, 0.0f, 1.0f, 1.0f, // bottom right
};
// create a vertex buffer object
unsigned int vbo;
glGenBuffers(1, &vbo);
// vertex array object
glGenVertexArrays(1, &g_VAO);
// 1. bind Vertex Array Object
glBindVertexArray(g_VAO);
// 2. copy our vertices array in a buffer for OpenGL to use
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(kVertices), kVertices, GL_STATIC_DRAW);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
// texture coord attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// vertex shader
const GLchar *vs_code = "#version 330 core\n" CODE(
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
void main() {
gl_Position = vec4(aPos, 1.0);
TexCoord = vec2(aTexCoord.x, aTexCoord.y);
}
);
unsigned int vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vs_code, NULL);
glCompileShader(vs);
int success;
char infolog[512];
glGetShaderiv(vs, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vs, 512, NULL, infolog);
printf("%s\n", infolog);
}
// fragment shader
const GLchar *fs_code = "#version 330 core\n" CODE(
out vec4 FragColor;
in vec2 TexCoord;
// texture samplers
uniform sampler2D texture1;
void main() {
FragColor = texture(texture1, TexCoord);
}
);
unsigned int fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fs_code, NULL);
glCompileShader(fs);
glGetShaderiv(fs, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fs, 512, NULL, infolog);
printf("%s\n", infolog);
}
// create program
int program = g_program = glCreateProgram();
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(program, 512, NULL, infolog);
printf("%s\n", infolog);
}
if (g_config.shader)
g_glsl_shader = GlslShader_CreateFromFile(g_config.shader);
return true;
}
static void OpenGLRenderer_Destroy() {
}
static void OpenGLRenderer_BeginDraw(int width, int height, uint8 **pixels, int *pitch) {
int size = width * height;
if (size > g_screen_buffer_size) {
g_screen_buffer_size = size;
free(g_screen_buffer);
g_screen_buffer = malloc(size * 4);
}
g_draw_width = width;
g_draw_height = height;
*pixels = g_screen_buffer;
*pitch = width * 4;
}
static void OpenGLRenderer_EndDraw() {
int drawable_width, drawable_height;
SDL_GL_GetDrawableSize(g_window, &drawable_width, &drawable_height);
int viewport_width = drawable_width, viewport_height = drawable_height;
if (!g_config.ignore_aspect_ratio) {
if (viewport_width * g_draw_height < viewport_height * g_draw_width)
viewport_height = viewport_width * g_draw_height / g_draw_width; // limit height
else
viewport_width = viewport_height * g_draw_width / g_draw_height; // limit width
}
int viewport_x = (drawable_width - viewport_width) >> 1;
int viewport_y = (viewport_height - viewport_height) >> 1;
glBindTexture(GL_TEXTURE_2D, g_texture.gl_texture);
if (g_draw_width == g_texture.width && g_draw_height == g_texture.height) {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, g_draw_width, g_draw_height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, g_screen_buffer);
} else {
g_texture.width = g_draw_width;
g_texture.height = g_draw_height;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, g_draw_width, g_draw_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, g_screen_buffer);
}
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
if (g_glsl_shader == NULL) {
glViewport(viewport_x, viewport_y, viewport_width, viewport_height);
glUseProgram(g_program);
int filter = g_config.linear_filtering ? GL_LINEAR : GL_NEAREST;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
glBindVertexArray(g_VAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
} else {
GlslShader_Render(g_glsl_shader, &g_texture, viewport_x, viewport_y, viewport_width, viewport_height);
}
SDL_GL_SwapWindow(g_window);
}
static const struct RendererFuncs kOpenGLRendererFuncs = {
&OpenGLRenderer_Init,
&OpenGLRenderer_Destroy,
&OpenGLRenderer_BeginDraw,
&OpenGLRenderer_EndDraw,
};
void OpenGLRenderer_Create(struct RendererFuncs *funcs) {
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, 3);
*funcs = kOpenGLRendererFuncs;
}