Last active
October 4, 2024 04:23
-
-
Save luke10x/cb640b1189e552444406747903092cac to your computer and use it in GitHub Desktop.
Gamedev examples
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
#include <GL/glew.h> | |
#include <GLFW/glfw3.h> | |
#include <iostream> | |
#ifdef __EMSCRIPTEN__ | |
#include <emscripten.h> | |
#endif | |
// Screen dimensions | |
const int SCREEN_WIDTH = 800; | |
const int SCREEN_HEIGHT = 600; | |
const char* HELLO_VERTEX_SHADER = | |
#ifdef __EMSCRIPTEN__ | |
"#version 300 es" | |
#else | |
"#version 330 core" | |
#endif | |
R"( | |
layout(location = 0) in vec3 aPos; | |
void main() { | |
gl_Position = vec4(aPos, 1.0); | |
} | |
)"; | |
const char* HELLO_FRAGMENT_SHADER = | |
#ifdef __EMSCRIPTEN__ | |
"#version 300 es" | |
#else | |
"#version 330 core" | |
#endif | |
R"( | |
precision mediump float; | |
out vec4 FragColor; | |
void main() { | |
FragColor = vec4(1.0, 0.5, 0.0, 1.0); // Orange color | |
} | |
)"; | |
// 1. OpenGL init subsystem | |
bool initVideo(); | |
void initBuffers(); | |
// 2. OpenGL shader subsystem | |
GLuint createShaderProgram( | |
const char* vertexShader, | |
const char* fragmentShader | |
); | |
GLuint compileShader(GLenum type, const char* source); | |
// 3. OpenGL diagnostic utils | |
void printShaderVersions(); | |
void checkOpenGLError(); | |
// 4. Main loop subsystem | |
void performOneCycle(); | |
void close(); | |
int main(int argc, char* argv[]); | |
// | |
// Global state context | |
// | |
typedef struct { | |
bool shouldContinue; | |
GLFWwindow* window; | |
GLuint triangleProgramId; | |
bool quit; | |
GLuint VAO, VBO; | |
} GameContext; | |
GameContext ctx; | |
// Implementation /////////////////// | |
// ************************** | |
// 1. OpenGL init subsystem | |
// ************************** | |
bool initVideo() { | |
// Initialize GLFW | |
if (!glfwInit()) { | |
printf("Failed to initialize GLFW\n"); | |
return -1; | |
} | |
glfwDefaultWindowHints(); | |
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); | |
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); | |
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); | |
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | |
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); | |
glfwWindowHint(GLFW_SAMPLES, 4); // enable multisampling | |
GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL ES 3.0", NULL, NULL); | |
if (window == NULL) { | |
printf("Failed to create window\n"); | |
glfwTerminate(); | |
return -1; | |
} | |
glfwMakeContextCurrent(window); | |
// Initialize GLEW to load OpenGL functions | |
glewExperimental = GL_TRUE; | |
if (glewInit() != GLEW_OK) { | |
printf("Error initializing GLEW\n"); | |
return -1; | |
} | |
printf("OpenGL Version: %s\n", glGetString(GL_VERSION)); | |
printf("GLSL Version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION)); | |
int width, height; | |
glfwGetFramebufferSize(window, &width, &height); | |
glViewport(0, 0, width, height); | |
ctx.window = window; | |
return true; | |
} | |
void initBuffers() { | |
GameContext* ctx_ = &ctx; | |
float vertices[] = { | |
-0.5f, -0.5f, 0.0f, // Bottom left | |
0.5f, -0.5f, 0.0f, // Bottom right | |
0.0f, 0.5f, 0.0f // Top | |
}; | |
GLuint VAO, VBO; | |
glGenVertexArrays(1, &VAO); | |
glGenBuffers(1, &VBO); | |
glBindVertexArray(VAO); | |
glBindBuffer(GL_ARRAY_BUFFER, VBO); | |
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); | |
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); | |
glEnableVertexAttribArray(0); | |
glBindBuffer(GL_ARRAY_BUFFER, 0); | |
glBindVertexArray(0); | |
ctx.VAO = VAO; | |
ctx.VBO = VBO; | |
}; | |
// **************************** | |
// 2. OpenGL shader subsystem | |
// **************************** | |
GLuint createShaderProgram( | |
const char* vertexShaderSource, | |
const char* fragmentShaderSource | |
) { | |
GLuint vertexShader = compileShader( | |
GL_VERTEX_SHADER, vertexShaderSource | |
); | |
GLuint fragmentShader = compileShader( | |
GL_FRAGMENT_SHADER, fragmentShaderSource | |
); | |
GLuint shaderProgram = glCreateProgram(); | |
glAttachShader(shaderProgram, vertexShader); | |
glAttachShader(shaderProgram, fragmentShader); | |
glLinkProgram(shaderProgram); | |
glDeleteShader(vertexShader); | |
glDeleteShader(fragmentShader); | |
return shaderProgram; | |
} | |
GLuint compileShader( | |
GLenum type, | |
const char* source | |
) { | |
GLuint shader = glCreateShader(type); | |
glShaderSource(shader, 1, &source, nullptr); | |
glCompileShader(shader); | |
GLint success; | |
glGetShaderiv(shader, GL_COMPILE_STATUS, &success); | |
if (!success) { | |
char infoLog[512]; | |
glGetShaderInfoLog(shader, 512, nullptr, infoLog); | |
std::cerr | |
<< "ERROR::SHADER::COMPILATION_FAILED\n" | |
<< "SHADER SOURCE:\n" | |
<< source | |
<< "\nSHADER TYPE: " | |
<< (type == GL_VERTEX_SHADER ? "Vertex Shader" : "") | |
<< (type == GL_FRAGMENT_SHADER ? "Fragment Shader" : "") | |
<< "\nSHADER COMPILATION ERROR:\n" | |
<< infoLog | |
<< std::endl; | |
exit(1); | |
} | |
return shader; | |
} | |
// **************************** | |
// 3. OpenGL diagnostic utils | |
// **************************** | |
void printShaderVersions() { | |
// Get OpenGL version | |
const GLubyte* glVersion = glGetString(GL_VERSION); | |
printf("OpenGL Version: %s\n", glVersion); | |
// Get GLSL (shader language) version | |
const GLubyte* glslVersion = glGetString(GL_SHADING_LANGUAGE_VERSION); | |
printf("GLSL (Shader) Version: %s\n", glslVersion); | |
// You can also get OpenGL vendor and renderer information | |
const GLubyte* vendor = glGetString(GL_VENDOR); | |
const GLubyte* renderer = glGetString(GL_RENDERER); | |
printf("Vendor: %s\n", vendor); | |
printf("Renderer: %s\n", renderer); | |
} | |
void checkOpenGLError() { | |
GLenum err; | |
while ((err = glGetError()) != GL_NO_ERROR) { | |
std::cerr << "OpenGL error: " << err << std::endl; | |
} | |
} | |
// ************************ | |
// 4. Main loop subsystem | |
// ************************ | |
void performOneCycle() { | |
ctx.shouldContinue = true; | |
if (glfwWindowShouldClose(ctx.window)) { | |
close(); | |
ctx.shouldContinue = false; | |
return; | |
} | |
// SDL_Event event; | |
// while (SDL_PollEvent(&event) != 0) { | |
// if (event.type == SDL_QUIT) { | |
// quit = true; | |
// } | |
// } | |
glClearColor(0.1f, 0.4f, 0.1f, 1.0f); | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
glUseProgram(ctx.triangleProgramId); | |
glBindVertexArray(ctx.VAO); | |
glDrawArrays(GL_TRIANGLES, 0, 3); | |
glBindVertexArray(0); | |
glfwPollEvents(); | |
// checkOpenGLError(); | |
glfwSwapBuffers(ctx.window); | |
} | |
void close() { | |
glfwDestroyWindow(ctx.window); | |
glfwTerminate(); | |
} | |
// Clean up resources | |
// void close(SDL_Window* window, SDL_GLContext context) { | |
// SDL_GL_DeleteContext(context); | |
// SDL_DestroyWindow(window); | |
// SDL_Quit(); | |
// } | |
int main(int argc, char* argv[]) { | |
ctx.shouldContinue = true; | |
if (!initVideo()) { | |
std::cerr << "Failed to initialize!" << std::endl; | |
return -1; | |
} | |
initBuffers(); | |
ctx.triangleProgramId = createShaderProgram( | |
HELLO_VERTEX_SHADER, | |
HELLO_FRAGMENT_SHADER | |
); | |
#ifdef __EMSCRIPTEN__ | |
emscripten_set_main_loop(performOneCycle, 0, 1); | |
#else | |
while (ctx.shouldContinue) performOneCycle(); | |
#endif | |
return 0; | |
// Should be no code beyond this point! | |
// singleLoopCycle() performs the cleanup | |
// when it detects that it should quit. | |
// The reason for this is that in Emscripten build | |
// this coude would be reached before | |
// singleLoopCycle() is even called, | |
// and the native code would reach here only | |
// after the main loop. | |
// | |
// The End. | |
} |
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
#include <GL/glew.h> | |
#ifdef __USE_SDL | |
#include <SDL2/SDL.h> | |
#elif defined(__USE_GLFW) | |
#include <GLFW/glfw3.h> | |
#endif | |
#include <iostream> | |
#ifdef __EMSCRIPTEN__ | |
#include <emscripten.h> | |
#endif | |
// *********** | |
// Constants | |
// *********** | |
// Screen dimensions | |
const int SCREEN_WIDTH = 800; | |
const int SCREEN_HEIGHT = 600; | |
const char* HELLO_VERTEX_SHADER = | |
#ifdef __EMSCRIPTEN__ | |
"#version 300 es" | |
#else | |
"#version 330 core" | |
#endif | |
R"( | |
layout(location = 0) in vec3 aPos; | |
void main() { | |
gl_Position = vec4(aPos, 1.0); | |
} | |
)"; | |
const char* HELLO_FRAGMENT_SHADER = | |
#ifdef __EMSCRIPTEN__ | |
"#version 300 es" | |
#else | |
"#version 330 core" | |
#endif | |
R"( | |
precision mediump float; | |
out vec4 FragColor; | |
void main() { | |
FragColor = vec4(1.0, 0.5, 0.0, 1.0); // Orange color | |
} | |
)"; | |
// ******************************* | |
// Declarations of all functions | |
// ******************************* | |
// 1. OpenGL init subsystem | |
bool initVideo(); | |
void initBuffers(); | |
// 2. OpenGL shader subsystem | |
GLuint createShaderProgram( | |
const char* vertexShader, | |
const char* fragmentShader | |
); | |
GLuint compileShader(GLenum type, const char* source); | |
// 3. OpenGL diagnostic utils | |
void printShaderVersions(); | |
void checkOpenGLError(); | |
// 4. Main loop subsystem | |
void performOneCycle(); | |
void close(); | |
int main(int argc, char* argv[]); | |
// ********************** | |
// Global state context | |
// ********************** | |
typedef struct { | |
bool shouldContinue; | |
#ifdef __USE_SDL | |
SDL_Window* sdlWindow; | |
SDL_GLContext sdlContext; | |
#elif defined(__USE_GLFW) | |
GLFWwindow* glfwWindow; | |
#endif | |
GLuint triangleProgramId; | |
bool quit; | |
GLuint VAO, VBO; | |
} GameContext; | |
GameContext ctx; | |
// ************************** | |
// 1. OpenGL init subsystem | |
// ************************** | |
bool initVideo() { | |
#ifdef __USE_SDL | |
if (SDL_Init(SDL_INIT_VIDEO) < 0) { | |
std::cerr << "SDL could not initialize! SDL Error: " << SDL_GetError() << std::endl; | |
return false; | |
} | |
#ifdef __EMSCRIPTEN__ | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); | |
#else | |
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); | |
#endif | |
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | |
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); | |
SDL_Window* window = SDL_CreateWindow( | |
"SDL2 OpenGL Home", | |
SDL_WINDOWPOS_CENTERED, | |
SDL_WINDOWPOS_CENTERED, | |
SCREEN_WIDTH, | |
SCREEN_HEIGHT, | |
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | |
); | |
if (window == nullptr) { | |
std::cerr << "Window could not be created! SDL Error: " << SDL_GetError() << std::endl; | |
return false; | |
} | |
std::cerr << " will create cont now " << std::endl; | |
SDL_GLContext context = SDL_GL_CreateContext(window); | |
if (context == nullptr) { | |
std::cerr << "OpenGL context could not be created! SDL Error: " << SDL_GetError() << std::endl; | |
return false; | |
} | |
std::cerr << " will make cont active now " << std::endl; | |
if (SDL_GL_MakeCurrent(window, context) < 0) { | |
std::cerr << "Could not make OpenGL context current: " << SDL_GetError() << std::endl; | |
SDL_Quit(); | |
} | |
std::cerr << " will create swap " << std::endl; | |
SDL_GL_SetSwapInterval(1); // Use V-Sync | |
#elif defined(__USE_GLFW) | |
if (!glfwInit()) { | |
printf("Failed to initialize GLFW\n"); | |
return -1; | |
} | |
glfwDefaultWindowHints(); | |
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); | |
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); | |
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); | |
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | |
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); | |
glfwWindowHint(GLFW_SAMPLES, 4); // enable multisampling | |
GLFWwindow* window = glfwCreateWindow( | |
SCREEN_WIDTH, | |
SCREEN_HEIGHT, | |
"GLFW OpenGL program", | |
NULL, NULL | |
); | |
if (window == nullptr) { | |
printf("Failed to create window\n"); | |
glfwTerminate(); | |
return -1; | |
} | |
glfwMakeContextCurrent(window); | |
glfwSwapInterval(1); // V-Sync | |
#endif | |
std::cerr << "creating glewn " << std::endl; | |
// Loading Glew is necessary no matter which graphics library you use | |
glewExperimental = GL_TRUE; | |
GLenum err = glewInit(); | |
if (err != GLEW_OK) { | |
std::cerr << "Error initializing GLEW! " << glewGetErrorString(err) << std::endl; | |
return false; | |
} | |
std::cerr << "glewn created " << std::endl; | |
int width, height; | |
#ifdef __USE_SDL | |
SDL_GL_GetDrawableSize(window, &width, &height); | |
ctx.sdlContext = context; | |
ctx.sdlWindow = window; | |
#elif defined(__USE_GLFW) | |
glfwGetFramebufferSize(window, &width, &height); | |
ctx.glfwWindow = window; | |
#endif | |
glViewport(0, 0, width, height); | |
std::cerr<<"Status: Using GLEW"<< glewGetString(GLEW_VERSION)<<'\n'; | |
printf("OpenGL Version: %s\n", glGetString(GL_VERSION)); | |
printf("GLSL Version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION)); | |
printShaderVersions(); | |
return true; | |
} | |
void initBuffers() { | |
GameContext* ctx_ = &ctx; | |
float vertices[] = { | |
-0.5f, -0.5f, 0.0f, // Bottom left | |
0.5f, -0.5f, 0.0f, // Bottom right | |
0.0f, 0.5f, 0.0f // Top | |
}; | |
GLuint VAO, VBO; | |
glGenVertexArrays(1, &VAO); | |
glGenBuffers(1, &VBO); | |
glBindVertexArray(VAO); | |
glBindBuffer(GL_ARRAY_BUFFER, VBO); | |
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); | |
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); | |
glEnableVertexAttribArray(0); | |
glBindBuffer(GL_ARRAY_BUFFER, 0); | |
glBindVertexArray(0); | |
ctx.VAO = VAO; | |
ctx.VBO = VBO; | |
}; | |
// **************************** | |
// 2. OpenGL shader subsystem | |
// **************************** | |
GLuint createShaderProgram( | |
const char* vertexShaderSource, | |
const char* fragmentShaderSource | |
) { | |
GLuint vertexShader = compileShader( | |
GL_VERTEX_SHADER, vertexShaderSource | |
); | |
GLuint fragmentShader = compileShader( | |
GL_FRAGMENT_SHADER, fragmentShaderSource | |
); | |
GLuint shaderProgram = glCreateProgram(); | |
glAttachShader(shaderProgram, vertexShader); | |
glAttachShader(shaderProgram, fragmentShader); | |
glLinkProgram(shaderProgram); | |
glDeleteShader(vertexShader); | |
glDeleteShader(fragmentShader); | |
return shaderProgram; | |
} | |
GLuint compileShader( | |
GLenum type, | |
const char* source | |
) { | |
GLuint shader = glCreateShader(type); | |
glShaderSource(shader, 1, &source, nullptr); | |
glCompileShader(shader); | |
GLint success; | |
glGetShaderiv(shader, GL_COMPILE_STATUS, &success); | |
if (!success) { | |
char infoLog[512]; | |
glGetShaderInfoLog(shader, 512, nullptr, infoLog); | |
std::cerr | |
<< "ERROR::SHADER::COMPILATION_FAILED\n" | |
<< "SHADER SOURCE:\n" | |
<< source | |
<< "\nSHADER TYPE: " | |
<< (type == GL_VERTEX_SHADER ? "Vertex Shader" : "") | |
<< (type == GL_FRAGMENT_SHADER ? "Fragment Shader" : "") | |
<< "\nSHADER COMPILATION ERROR:\n" | |
<< infoLog | |
<< std::endl; | |
exit(1); | |
} | |
return shader; | |
} | |
// **************************** | |
// 3. OpenGL diagnostic utils | |
// **************************** | |
void printShaderVersions() { | |
// Get OpenGL version | |
const GLubyte* glVersion = glGetString(GL_VERSION); | |
printf("OpenGL Version: %s\n", glVersion); | |
// Get GLSL (shader language) version | |
const GLubyte* glslVersion = glGetString(GL_SHADING_LANGUAGE_VERSION); | |
printf("GLSL (Shader) Version: %s\n", glslVersion); | |
// You can also get OpenGL vendor and renderer information | |
const GLubyte* vendor = glGetString(GL_VENDOR); | |
const GLubyte* renderer = glGetString(GL_RENDERER); | |
printf("Vendor: %s\n", vendor); | |
printf("Renderer: %s\n", renderer); | |
} | |
void checkOpenGLError() { | |
GLenum err; | |
while ((err = glGetError()) != GL_NO_ERROR) { | |
std::cerr << "OpenGL error: " << err << std::endl; | |
} | |
} | |
// ************************ | |
// 4. Main loop subsystem | |
// ************************ | |
void performOneCycle() { | |
ctx.shouldContinue = true; | |
#ifdef __USE_SDL | |
SDL_Event event; | |
while (SDL_PollEvent(&event) != 0) { | |
if (event.type == SDL_QUIT) { | |
close(); | |
ctx.shouldContinue = false; | |
return; | |
} | |
if (event.type == SDL_KEYDOWN) { | |
if (event.key.keysym.sym == SDLK_ESCAPE) { | |
close(); | |
ctx.shouldContinue = false; | |
return; | |
} | |
} | |
} | |
#elif defined(__USE_GLFW) | |
if (glfwWindowShouldClose(ctx.glfwWindow)) { | |
close(); | |
ctx.shouldContinue = false; | |
return; | |
} | |
if (glfwGetKey(ctx.glfwWindow, GLFW_KEY_ESCAPE) == GLFW_PRESS) { | |
glfwSetWindowShouldClose(ctx.glfwWindow, true); // Mark window to close | |
} | |
#endif | |
glClearColor(0.1f, 0.4f, 0.1f, 1.0f); | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
glUseProgram(ctx.triangleProgramId); | |
glBindVertexArray(ctx.VAO); | |
glDrawArrays(GL_TRIANGLES, 0, 3); | |
glBindVertexArray(0); | |
checkOpenGLError(); | |
#ifdef __USE_GLFW | |
glfwPollEvents(); | |
#endif | |
#ifdef __USE_SDL | |
SDL_GL_SwapWindow(ctx.sdlWindow); | |
#elif defined(__USE_GLFW) | |
glfwSwapBuffers(ctx.glfwWindow); | |
#endif | |
} | |
void close() { | |
#ifdef __USE_SDL | |
SDL_GL_DeleteContext(ctx.sdlContext); | |
SDL_DestroyWindow(ctx.sdlWindow); | |
SDL_Quit(); | |
#elif defined(__USE_GLFW) | |
glfwDestroyWindow(ctx.glfwWindow); | |
glfwTerminate(); | |
#endif | |
} | |
int main(int argc, char* argv[]) { | |
ctx.shouldContinue = true; | |
if (!initVideo()) { | |
std::cerr << "Failed to initialize!" << std::endl; | |
return -1; | |
} | |
initBuffers(); | |
ctx.triangleProgramId = createShaderProgram( | |
HELLO_VERTEX_SHADER, | |
HELLO_FRAGMENT_SHADER | |
); | |
#ifdef __EMSCRIPTEN__ | |
emscripten_set_main_loop(performOneCycle, 0, 1); | |
#else | |
while (ctx.shouldContinue) performOneCycle(); | |
#endif | |
return 0; | |
// Should be no code beyond this point! | |
// singleLoopCycle() performs the cleanup | |
// when it detects that it should quit. | |
// The reason for this is that in Emscripten build | |
// this coude would be reached before | |
// singleLoopCycle() is even called, | |
// and the native code would reach here only | |
// after the main loop. | |
// | |
// The End. | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment