// GLAD #include "glad/glad.h" // GLM #define GLM_ENABLE_EXPERIMENTAL #include "glm/ext/matrix_transform.hpp" #include "glm/ext/vector_float3.hpp" #include "glm/gtc/type_ptr.hpp" #include "glm/trigonometric.hpp" #include "glm/gtx/rotate_vector.hpp" #include "glm/gtx/string_cast.hpp" // SDL #include #include #include #include #include #include #include #include // STDLIB #include #include #include #include #include #define WINDOW_WIDTH 1280 #define WINDOW_HEIGHT 720 #define WINDOW_HALF_WIDTH 640 #define WINDOW_HALF_HEIGHT 360 #define min(a, b) (a < b ? a : b) #define max(a, b) (a > b ? a : b) #define clamp(v, a, b) (min(max(v, a), b)) enum exit_codes : int { EXIT_CODE_SUCCESS, EXIT_CODE_SDL_INIT_FAILED, EXIT_CODE_WINDOW_CREATION_FAILED, EXIT_CODE_OPENGL_CONTEXT_FAILED, EXIT_CODE_GLAD_LOADER_FAILED, }; class Shader { public: Shader(const std::string &vert_file, const std::string &frag_file); ~Shader(); void activate(); void set_float(const char *name, float value); void set_vec3(const char *name, glm::vec3 vector); void set_mat3(const char *name, glm::mat3 matrix); void set_mat4(const char *name, glm::mat4 matrix); GLuint program; private: void link_program(GLuint vert, GLuint frag); GLuint load_and_compile_shader(const std::string &filepath, GLenum shader_type); std::string load_shader_from_file(const std::string &filepath); static const char *get_shader_type_string(GLenum shader_type); }; int main() { if (SDL_Init(SDL_INIT_EVERYTHING) != 0) { return EXIT_CODE_SDL_INIT_FAILED; } SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_Window *window = SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); if (!window) { return EXIT_CODE_WINDOW_CREATION_FAILED; } SDL_GLContext context = SDL_GL_CreateContext(window); if (!context) { return EXIT_CODE_OPENGL_CONTEXT_FAILED; } if (gladLoadGLLoader(SDL_GL_GetProcAddress) == 0) { return EXIT_CODE_GLAD_LOADER_FAILED; } SDL_SetRelativeMouseMode(SDL_TRUE); SDL_WarpMouseInWindow(window, WINDOW_HALF_WIDTH, WINDOW_HALF_HEIGHT); glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); glEnable(GL_DEPTH_TEST); std::vector vertices = { 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, // position, normal 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, // position, normal 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, // position, normal 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, // position, normal -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, // position, normal 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // position, normal -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, // position, normal -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, // position, normal -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, // position, normal 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, // position, normal 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, // position, normal 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, // position, normal 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, // position, normal -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // position, normal -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, // position, normal -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // position, normal -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, // position, normal 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // position, normal -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, // position, normal 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, // position, normal -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, // position, normal -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, // position, normal 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, // position, normal -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // position, normal -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, // position, normal }; std::vector indices = { 20, 11, 0, 0, 6, 20, 7, 1, 3, 3, 16, 7, 24, 14, 21, 21, 4, 24, 2, 12, 10, 10, 22, 2, 8, 9, 19, 19, 18, 8, 13, 5, 17, 17, 23, 15 }; GLuint vao = 0; glGenVertexArrays(1, &vao); glBindVertexArray(vao); GLuint ebo = 0; glGenBuffers(1, &ebo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW); GLuint vbo = 0; glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GLfloat), vertices.data(), GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void *)0); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void *)(3 * sizeof(GLfloat))); glEnableVertexAttribArray(1); GLuint light_vao = 0; glGenVertexArrays(1, &light_vao); glBindVertexArray(light_vao); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void *)0); glEnableVertexAttribArray(0); glBindVertexArray(0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); Shader main_shader {"shaders/vert.glsl", "shaders/frag.glsl"}; Shader light_shader {"shaders/vert.glsl", "shaders/light_frag.glsl"}; const float camera_speed = 25.0f; glm::vec3 camera_position = glm::vec3(-2.0f, 0.0f, 6.0f); glm::vec3 camera_forward = glm::vec3(0.0f); glm::vec3 world_up = glm::vec3(0.0f, 1.0f, 0.0f); glm::vec3 light_position = glm::vec3(1.0f, 0.5f, 0.0f); glm::vec3 light_diffuse = glm::vec3(0.5f, 0.5f, 0.5f); float yaw = -70.0f; float pitch = 0.0f; glm::mat4 model = glm::mat4(1.0f); glm::mat4 view = glm::mat4(1.0f); glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 0.1f, 100.0f); glm::mat3 normal_mat = glm::mat3(1.0f); main_shader.set_mat4 ("projection", projection); main_shader.set_vec3 ("material.ambient", glm::vec3(1.0f, 0.5f, 0.31f)); main_shader.set_vec3 ("material.diffuse", glm::vec3(1.0f, 0.5f, 0.31f)); main_shader.set_vec3 ("material.specular", glm::vec3(0.5f, 0.5f, 0.5f)); main_shader.set_float("material.shininess", 32.0f); main_shader.set_vec3 ("light.ambient", glm::vec3(0.2f, 0.2f, 0.2f)); main_shader.set_vec3 ("light.diffuse", light_diffuse); main_shader.set_vec3 ("light.specular", glm::vec3(1.0f, 1.0f, 1.0f)); light_shader.set_mat4("projection", projection); const float light_radius = 3.0f; const float sensitivity = 0.1f; int last_mouse_x = WINDOW_HALF_WIDTH; int last_mouse_y = WINDOW_HALF_HEIGHT; uint32_t last_frame = SDL_GetTicks(); float delta = 0.0f; bool running = true; SDL_Event event = {}; while (running) { uint32_t ticks = SDL_GetTicks(); float seconds = ticks * 0.001f; delta = (ticks - last_frame) * 0.001f; light_position = glm::vec3(sin(seconds) * light_radius, light_position.y, cos(seconds) * light_radius); light_diffuse = glm::vec3( clamp(sin(seconds * 3.0f) + 1.0f, 0.0f, 1.0f), clamp(cos(seconds * 8.0f) + 1.0f, 0.0f, 1.0f), clamp(tan(seconds * 5.0f) + 3.0f, 0.0f, 1.0f) ); last_frame = ticks; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: running = false; break; case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_ESCAPE) { running = false; } else if (event.key.keysym.sym == SDLK_w) { camera_position += camera_speed * delta * camera_forward; } else if (event.key.keysym.sym == SDLK_s) { camera_position -= camera_speed * delta * camera_forward; } else if (event.key.keysym.sym == SDLK_d) { camera_position += camera_speed * delta * glm::normalize(glm::cross(camera_forward, world_up)); } else if (event.key.keysym.sym == SDLK_a) { camera_position -= camera_speed * delta * glm::normalize(glm::cross(camera_forward, world_up)); } break; case SDL_MOUSEMOTION: { float x_offset = event.motion.xrel; float y_offset = -event.motion.yrel; last_mouse_x = last_mouse_x + event.motion.xrel; last_mouse_y = last_mouse_y + event.motion.yrel; x_offset *= sensitivity; y_offset *= sensitivity; yaw += x_offset; pitch += y_offset; if(pitch > 89.0f) { pitch = 89.0f; } if(pitch < -89.0f) { pitch = -89.0f; } } break; case SDL_WINDOWEVENT: if (event.window.event == SDL_WINDOWEVENT_RESIZED) { SDL_Window *wnd = SDL_GetWindowFromID(event.window.windowID); if (!wnd) { continue; } int w, h; SDL_GL_GetDrawableSize(wnd, &w, &h); glViewport(0, 0, w, h); SDL_WarpMouseInWindow(wnd, (int)(w * 0.5f), (int)(h * 0.5f)); } break; } } camera_forward.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch)); camera_forward.y = sin(glm::radians(pitch)); camera_forward.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch)); camera_forward = glm::normalize(camera_forward); view = glm::lookAt(camera_position, camera_position + camera_forward, world_up); main_shader.set_vec3("camera_position", camera_position); main_shader.set_mat4("view", view); light_shader.set_mat4("view", view); glClearColor(0.04f, 0.08f, 0.08f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); model = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f)); normal_mat = glm::transpose(glm::inverse(model)); main_shader.activate(); main_shader.set_vec3("light.position", light_position); main_shader.set_vec3("light.diffuse", light_diffuse); main_shader.set_mat4("model", model); main_shader.set_mat3("normal_mat", normal_mat); glBindVertexArray(vao); glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, (void *)0); model = glm::translate(glm::mat4(1.0f), light_position); model = glm::scale(model, glm::vec3(0.2f)); light_shader.activate(); light_shader.set_mat4("model", model); light_shader.set_vec3("light_diffuse", light_diffuse); glBindVertexArray(light_vao); glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, (void *)0); SDL_GL_SwapWindow(window); } SDL_GL_DeleteContext(context); SDL_DestroyWindow(window); SDL_Quit(); return EXIT_CODE_SUCCESS; } Shader::Shader(const std::string &vert_file, const std::string &frag_file) { GLuint vert = load_and_compile_shader(vert_file, GL_VERTEX_SHADER); GLuint frag = load_and_compile_shader(frag_file, GL_FRAGMENT_SHADER); link_program(vert, frag); glDeleteShader(vert); glDeleteShader(frag); } Shader::~Shader() { if (program > 0) { glDeleteProgram(program); } } void Shader::activate() { if (program > 0) { glUseProgram(program); } } void Shader::set_float(const char *name, float value) { activate(); glUniform1f(glGetUniformLocation(program, name), value); } void Shader::set_vec3(const char *name, glm::vec3 vector) { activate(); glUniform3f(glGetUniformLocation(program, name), vector.x, vector.y, vector.z); } void Shader::set_mat3(const char *name, glm::mat3 matrix) { activate(); glUniformMatrix3fv(glGetUniformLocation(program, name), 1, GL_FALSE, glm::value_ptr(matrix)); } void Shader::set_mat4(const char *name, glm::mat4 matrix) { activate(); glUniformMatrix4fv(glGetUniformLocation(program, name), 1, GL_FALSE, glm::value_ptr(matrix)); } void Shader::link_program(GLuint vert, GLuint frag) { program = glCreateProgram(); glAttachShader(program, vert); glAttachShader(program, frag); glLinkProgram(program); GLint program_linked; glGetProgramiv(program, GL_LINK_STATUS, &program_linked); if (program_linked != GL_TRUE) { GLsizei log_length = 0; GLchar message[1024]; glGetProgramInfoLog(program, 1024, &log_length, message); printf("Failed to link program: %s\n", message); program = 0; } } GLuint Shader::load_and_compile_shader(const std::string &filepath, GLenum shader_type) { std::string src = load_shader_from_file(filepath); const char *shader_src = src.c_str(); GLuint shader = glCreateShader(shader_type); glShaderSource(shader, 1, &shader_src, NULL); glCompileShader(shader); GLint shader_compiled; glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_compiled); if (shader_compiled != GL_TRUE) { GLsizei log_length = 0; GLchar message[1024]; glGetShaderInfoLog(shader, 1024, &log_length, message); printf("Failed to compile %s shader: %s\n", get_shader_type_string(shader_type), message); return 0; } return shader; } std::string Shader::load_shader_from_file(const std::string &filepath) { FILE *fp = fopen(filepath.c_str(), "r"); if (!fp) { return ""; } std::string output = {}; char buf[1024] = {0}; while (fgets(buf, sizeof(buf), fp)) { output += buf; } return output; } const char *Shader::get_shader_type_string(GLenum shader_type) { const char *output; switch (shader_type) { case GL_COMPUTE_SHADER: output = "compute"; break; case GL_VERTEX_SHADER: output = "vertex"; break; case GL_TESS_CONTROL_SHADER: output = "tess_control"; break; case GL_TESS_EVALUATION_SHADER: output = "tess_evaluation"; break; case GL_GEOMETRY_SHADER: output = "geometry"; break; case GL_FRAGMENT_SHADER: output = "fragment"; break; default: output = "UNKNOWN"; break; } return output; }