Created
September 10, 2021 22:46
-
-
Save dmoa/0f6eda587d8bf3ae3403df91a62a7c23 to your computer and use it in GitHub Desktop.
OpenGL Holy Grail
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This example loads an image and a shader that are used to draw the image. | |
#include <math.h> | |
#include <GL/glew.h> | |
#include <SDL2/SDL.h> | |
#include <SDL2/SDL_image.h> | |
#include <SDL2/SDL_opengl.h> | |
#define print SDL_Log | |
#define STB_IMAGE_IMPLEMENTATION | |
#define STBI_ONLY_PNG | |
#include "stb_image.h" | |
char* gl_error_string(GLenum err); | |
char* LoadFile(char* path); | |
GLuint LoadImage(char* path); | |
GLuint LoadShader(char* path, GLenum shader_type); | |
struct Graphics { | |
SDL_Window* window = NULL; | |
SDL_Renderer* renderer = NULL; | |
SDL_GLContext gl_context = NULL; | |
void Init() { | |
SDL_Init(SDL_INIT_VIDEO); | |
// window | |
window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 800, SDL_WINDOW_OPENGL); | |
gl_context = SDL_GL_CreateContext(window); | |
glClearColor(0.5, 0.5, 0.5, 1); | |
if (glewInit() != GLEW_OK) { | |
print("Glew failed to init!"); | |
exit(EXIT_FAILURE); | |
} | |
char* glsl_v = glGetString(GL_SHADING_LANGUAGE_VERSION); | |
print("glsl version: %s", glsl_v); | |
// misc | |
glEnable(GL_TEXTURE_2D); | |
stbi_set_flip_vertically_on_load(true); | |
// transparency | |
glEnable(GL_BLEND); | |
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
} | |
void Shutdown() { | |
SDL_GL_DeleteContext(gl_context); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
} | |
}; | |
int main(int argc, char *argv[]) { | |
Graphics graphics; | |
graphics.Init(); | |
GLuint texture = LoadImage("assets/chess.png"); | |
// Shader Loading | |
GLuint frag = LoadShader("src/shader.frag", GL_FRAGMENT_SHADER); | |
GLuint vert = LoadShader("src/shader.vert", GL_VERTEX_SHADER); | |
GLuint program = glCreateProgram(); | |
glAttachShader(program, frag); | |
glAttachShader(program, vert); | |
glLinkProgram(program); | |
// Checking for program errors. | |
GLint status; | |
glValidateProgram(program); | |
glGetProgramiv(program, GL_LINK_STATUS, & status); | |
if (!status) { | |
GLint len; | |
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len); | |
if (len > 1) { | |
char *log; | |
log = malloc(len); | |
glGetProgramInfoLog(program, len, &len, log); | |
fprintf(stderr, "%s\n\n", log); | |
free(log); | |
} | |
print("Failed to link program!"); | |
} | |
glUseProgram(program); | |
print("%i", glGetUniformLocation(program, "pixel_texture")); | |
glUniform1i(glGetUniformLocation(program, "pixel_texture"), 0); | |
bool quit = false; | |
SDL_Event event; | |
while (! quit) { | |
while (SDL_PollEvent(& event)) { | |
if (event.type == SDL_QUIT) { | |
quit = true; | |
} | |
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) { | |
quit = true; | |
} | |
} | |
glClear(GL_COLOR_BUFFER_BIT); | |
glBindTexture(GL_TEXTURE_2D, texture); | |
glBegin(GL_QUADS); // Draw textured Quad | |
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 0.0); // Top Left Corner | |
glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, 1.0, 0.0); // Top Right Corner | |
glTexCoord2f(1.0, 0.0); glVertex3f( 1.0,-1.0, 0.0); // Bottom Right Corner | |
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0,-1.0, 0.0); // Bottom Left Corner | |
glEnd(); | |
SDL_GL_SwapWindow(graphics.window); | |
SDL_Delay(10); | |
} | |
glDeleteTextures(1, & texture); | |
graphics.Shutdown(); | |
return 0; | |
} | |
char* LoadFile(char* path) { | |
FILE* file = fopen(path, "r"); | |
if (! file) { | |
print("Couldn't load %s !", file); | |
return NULL; | |
} | |
// 0L because fseek takes a long integer, but we still only want zero | |
fseek(file, 0L, SEEK_END); | |
long file_size = ftell(file); | |
fseek(file, 0L, SEEK_SET); | |
char* buffer = (char*) calloc(file_size, sizeof(char)); | |
fread(buffer, sizeof(char), file_size, file); | |
// buffer[file_size] = '\0'; // Text files usually don't have the terminating string character. | |
fclose(file); | |
return buffer; | |
} | |
GLuint LoadImage(char* path) { | |
int width; int height; int num_channels; | |
void* pixels = stbi_load(path, & width, & height, & num_channels, 0); | |
if (! pixels) { | |
print("Couldn't load %s", path); | |
return -1; | |
} | |
GLuint texture; | |
glGenTextures(1, & texture); | |
glActiveTexture(GL_TEXTURE0); | |
glBindTexture(GL_TEXTURE_2D, texture); | |
// I'm 90% sure the rest of this function just sets what would be the default parameters, | |
// but I'm doing this just in case. | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); | |
auto format = (num_channels == 3) ? GL_RGB : GL_RGBA; | |
glTexImage2D( | |
GL_TEXTURE_2D, // target | |
0, // level, 0 = base, no minimap, | |
format, // internal format | |
width, // width | |
height, // height | |
0, // This value must be 0. | |
format, // format | |
GL_UNSIGNED_BYTE, // data type of the pixel datatype | |
pixels | |
); | |
// glGenerateMipmap(GL_TEXTURE_2D); | |
stbi_image_free(pixels); | |
return texture; | |
} | |
GLuint LoadShader(char* path, GLenum shader_type) { | |
char* file = LoadFile(path); | |
GLuint shader = glCreateShader(shader_type); | |
glShaderSource(shader, 1, & file, NULL); | |
glCompileShader(shader); | |
free(file); | |
// Check for errors: | |
GLint success; GLint length; | |
glGetShaderiv(shader, GL_COMPILE_STATUS, & success); | |
if (! success) { | |
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, & length); | |
if (length) { | |
char* log = (char*) malloc(length); | |
glGetShaderInfoLog(shader, length, NULL, log); | |
print("%s\n\n", log); | |
} | |
print("Error compiling shader %s.", path); | |
} | |
return shader; | |
} | |
char* gl_error_string(GLenum err) | |
{ | |
switch (err) | |
{ | |
// opengl 2 errors | |
case GL_NO_ERROR: | |
return "GL_NO_ERROR"; | |
case GL_INVALID_ENUM: | |
return "GL_INVALID_ENUM"; | |
case GL_INVALID_VALUE: | |
return "GL_INVALID_VALUE"; | |
case GL_INVALID_OPERATION: | |
return "GL_INVALID_OPERATION"; | |
case GL_STACK_OVERFLOW: | |
return "GL_STACK_OVERFLOW"; | |
case GL_STACK_UNDERFLOW: | |
return "GL_STACK_UNDERFLOW"; | |
case GL_OUT_OF_MEMORY: | |
return "GL_OUT_OF_MEMORY"; | |
case GL_TABLE_TOO_LARGE: | |
return "GL_TABLE_TOO_LARGE"; | |
// opengl 3 errors | |
case GL_INVALID_FRAMEBUFFER_OPERATION: | |
return "GL_INVALID_FRAMEBUFFER_OPERATION"; | |
default: | |
print("unknown error"); | |
return NULL; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment