From 46df79ca2a58149667524be4f16e71df52b7fa1d Mon Sep 17 00:00:00 2001 From: Jackson H Date: Tue, 4 Jun 2024 10:31:34 -0500 Subject: [PATCH] init --- .gitignore | 15 ++ CMakeLists.txt | 22 +++ Camera.h | 58 +++++++ Object3D.cpp | 282 +++++++++++++++++++++++++++++++ Object3D.h | 26 +++ README.md | 8 + Shader.h | 178 ++++++++++++++++++++ main.cpp | 393 ++++++++++++++++++++++++++++++++++++++++++++ shaders/shader.frag | 50 ++++++ shaders/shader.vert | 22 +++ 10 files changed, 1054 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 Camera.h create mode 100644 Object3D.cpp create mode 100644 Object3D.h create mode 100644 README.md create mode 100644 Shader.h create mode 100644 main.cpp create mode 100644 shaders/shader.frag create mode 100644 shaders/shader.vert diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d4feb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.idea +.vscode + +build +glad.dir +include +lib +main.dir +src/ + +*.bat +*.sh +glad.c + +objs diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f2f5b0a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.28) +project(OpenGL_Practice) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +file(COPY objs/ DESTINATION objs/) +file(COPY shaders/ DESTINATION shaders/) + +add_library(glad STATIC include/glad/glad.h include/KHR/khrplatform.h) +target_include_directories(glad PUBLIC include) +set_target_properties(glad PROPERTIES LINKER_LANGUAGE CXX) + +add_subdirectory(src/glfw-3.4) + +add_executable(main glad.c main.cpp + Object3D.cpp + Object3D.h + Shader.h + Camera.h) +target_link_libraries(main PUBLIC glfw glad) + diff --git a/Camera.h b/Camera.h new file mode 100644 index 0000000..608f964 --- /dev/null +++ b/Camera.h @@ -0,0 +1,58 @@ +#ifndef CAMERA_H +#define CAMERA_H + +#include +#include + +class Camera +{ + public: + glm::vec3 position; + glm::vec3 target; + glm::vec3 direction; + glm::vec3 up; + float speed; + + glm::vec3 worldUp; + + Camera(float speed) + { + this->speed = speed; + + this->position = glm::vec3(0.0f, 0.0f, 3.0f); + this->target = glm::vec3(0.0f, 0.0f, -1.0f); + + this->direction = glm::normalize(this->position); + this->worldUp = glm::vec3(0.0f, 1.0f, 0.0f); + + glm::vec3 right = glm::normalize(glm::cross(up, this->direction)); + this->up = glm::cross(this->direction, right); + } + + Camera(glm::vec3 pos, glm::vec3 target, float speed) + { + this->speed = speed; + + this->position = pos; + this->target = target; + + this->direction = glm::normalize(this->position - this->target); + this->worldUp = glm::vec3(0.0f, 1.0f, 0.0f); + + glm::vec3 right = glm::normalize(glm::cross(up, this->direction)); + this->up = glm::cross(this->direction, right); + } + + glm::mat4 getView() + { + return glm::lookAt(this->position, + this->target + this->position, + this->worldUp); + } + +}; + +#endif + + + diff --git a/Object3D.cpp b/Object3D.cpp new file mode 100644 index 0000000..e265a7d --- /dev/null +++ b/Object3D.cpp @@ -0,0 +1,282 @@ +#include "Object3D.h" + +#include +#include +#include + +Object3D::Object3D() = default; + +Object3D::Object3D(const std::string &file_path) +{ + this->loadFromOBJ(file_path); +} + +bool Object3D::loadFromOBJ(const std::string& file_path) +{ + std::ifstream f(file_path); + if (!f.is_open()) + return false; + + std::vector temp_vertices; + std::vector temp_indices; + std::vector temp_normals; + + while (!f.eof()) + { + char line[128]; + f.getline(line, 128); + + std::string l(line); + + // std::cout << "LINE: " << l << std::endl; + + if (line[0] == 'f') + { + l.erase(0, 2); // Erase the "f " + size_t pos1 = 0; + std::string token1; + + // Split by space + while ((pos1 = l.find(' ')) != std::string::npos) + { + token1 = l.substr(0, pos1); + int face_vert; + + if (l.find("//") != std::string::npos) + { + size_t pos2 = 0; + std::string token2; + bool face = true; + + while ((pos2 = token1.find("//")) != std::string::npos) + { + token2 = token1.substr(0, pos2); + // std::cout << token1 << std::endl; + + if (face) + { + face_vert = std::stoi(token2) - 1; + temp_indices.push_back(face_vert); + face = false; + + } + + token1.erase(0, pos2 + 2); + } + + // The Normal paired with the face is here + if (!face) { + // std::cout << "thisg" << std::endl; + int face_normal = std::stoi(token1) - 1; + // std::cout << "f " << face_vert + 1 << "//" << face_normal + 1 << std::endl; + + bool VBO_el_found = false; + + // If Vertice AND correct normal are already in VBO buffer just refernce that + for (unsigned int i = 0; i < this->VBO_buffer.size(); i += 6) + { + bool vert_equal = true; + bool normal_equal = true; + + // Since each Vertice & Normal are 3 elements we need to do a little looping + for (int a = 0; a < 3; a++) + { + if (this->VBO_buffer[i + a] != temp_vertices[(face_vert * 3) + a]) { + vert_equal = false; + break; + } + } + + for (int a = 3; a < 6; a++) + { + if (this->VBO_buffer[i + a] != temp_normals[(face_normal * 3) + a - 3]) { + normal_equal = false; + break; + } + } + + if (vert_equal && normal_equal) { + this->EBO_buffer.push_back(i / 6); + VBO_el_found = true; + } + } + + // If not we need to add it to the VBO buffer + if (!VBO_el_found) + { + // Again we need to add all three elements for each + for (int a = 0; a < 3; a++) + this->VBO_buffer.push_back(temp_vertices[face_vert * 3 + a]); + + + for (int a = 0; a < 3; a++) + this->VBO_buffer.push_back(temp_normals[face_normal * 3 + a]); + + this->EBO_buffer.push_back(this->VBO_buffer.size() / 6 - 1); + } + } + + // std::cout << l << std::endl; + + // temp_indices.push_back(std::stoi(token1) - 1); + + } + + l.erase(0, pos1 + 1); + } + + // std::cout << "thing" << std::endl; + // std::cout << l << std::endl; + + if (l.find("//") != std::string::npos) + { + size_t pos2 = 0; + std::string token2; + bool face = true; + int face_vert; + + while ((pos2 = l.find("//")) != std::string::npos) + { + token2 = l.substr(0, pos2); + face_vert = std::stoi(token2) - 1; + + + if (face) + { + temp_indices.push_back(face_vert); + face = false; + } + // Else case if I want to add normals for the faces + + l.erase(0, pos2 + 2); + } + + // The Normal paired with the face is here + if (!face) { + // std::cout << "thisg" << std::endl; + int face_normal = std::stoi(l) - 1; + // std::cout << "f " << face_vert + 1 << "//" << face_normal + 1 << std::endl; + + bool VBO_el_found = false; + + // If Vertice AND correct normal are already in VBO buffer just refernce that + for (unsigned int i = 0; i < this->VBO_buffer.size(); i += 6) + { + bool vert_equal = true; + bool normal_equal = true; + + // Since each Vertice & Normal are 3 elements we need to do a little looping + for (int a = 0; a < 3; a++) + { + if (this->VBO_buffer[i + a] != temp_vertices[(face_vert * 3) + a]) { + vert_equal = false; + break; + } + } + + for (int a = 3; a < 6; a++) + { + if (this->VBO_buffer[i + a] != temp_normals[(face_normal * 3) + a - 3]) { + normal_equal = false; + break; + } + } + + if (vert_equal && normal_equal) { + this->EBO_buffer.push_back(i / 6); + VBO_el_found = true; + } + } + + // If not we need to add it to the VBO buffer + if (!VBO_el_found) + { + // Again we need to add all three elements for each + for (int a = 0; a < 3; a++) + this->VBO_buffer.push_back(temp_vertices[face_vert * 3 + a]); + + + for (int a = 0; a < 3; a++) + this->VBO_buffer.push_back(temp_normals[face_normal * 3 + a]); + + this->EBO_buffer.push_back(this->VBO_buffer.size() / 6 - 1); + } + } + + // std::cout << l << std::endl; + + // temp_indices.push_back(std::stoi(token1) - 1); + + } + + } + else if (line[0] == 'v') + { + + + size_t pos1 = 0; + std::string token1; + bool normal = line[1] == 'n'; + + if (normal) + { + l.erase(0, 3); // Get rid of the "vn " + } + else + { + l.erase(0, 2); // Get rid of the "v " + } + + + // Split by space + while ((pos1 = l.find(' ')) != std::string::npos) + { + token1 = l.substr(0, pos1); + + if (normal) + { + temp_normals.push_back(std::stof(token1)); + } + else + { + temp_vertices.push_back(std::stof(token1)); + } + + + l.erase(0, pos1 + 1); + } + + // Last point is still left after the loop + if (normal) + { + temp_normals.push_back(std::stof(l)); + } + else + { + temp_vertices.push_back(std::stof(l)); + } + } + } + + this->vertices = temp_vertices; + this->indices = temp_indices; + this->normals = temp_normals; + + return true; +} + +std::vector Object3D::getVBOBuffer() +{ + std::vector temp_VBO_buffer; + + for (int i = 0; i < this->vertices.size() / 3; i++) + { + for (int a = 0; a < 3; a++) + temp_VBO_buffer.push_back(this->vertices[(i * 3) + a]); + for (int a = 0; a < 3; a++) + temp_VBO_buffer.push_back(this->normals[(i * 3) + a]); + } + + return temp_VBO_buffer; +} + diff --git a/Object3D.h b/Object3D.h new file mode 100644 index 0000000..1873e8b --- /dev/null +++ b/Object3D.h @@ -0,0 +1,26 @@ +#ifndef OBJECT3D_H +#define OBJECT3D_H +#include +#include + + +class Object3D { + public: + std::vector vertices; + std::vector indices; + std::vector normals; + + std::vector EBO_buffer; + std::vector VBO_buffer; + + Object3D(); + Object3D(const std::string &file_path); + + bool loadFromOBJ(const std::string& file_path); + std::vector getVBOBuffer(); + +}; + + + +#endif //OBJECT3D_H diff --git a/README.md b/README.md new file mode 100644 index 0000000..d436764 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ + +# About +This project requires GLM, Glad, and GLFW. + +I use / used (depending on the time) this repo for messing with OpenGL while getting a grasp on graphics programming. +A large portion of this code was made following the tutorial at https://learnopengl.com, which I would totally reccomend! + + diff --git a/Shader.h b/Shader.h new file mode 100644 index 0000000..ef7853d --- /dev/null +++ b/Shader.h @@ -0,0 +1,178 @@ +#ifndef SHADER_H +#define SHADER_H + +#include + +#include +#include +#include +#include +#include + +class Shader +{ +public: + unsigned int ID; + // constructor generates the shader on the fly + // ------------------------------------------------------------------------ + Shader(const std::string &vertexPath, const std::string &fragmentPath) + { + // 1. retrieve the vertex/fragment source code from filePath + std::string vertexCode; + std::string fragmentCode; + std::ifstream vShaderFile; + std::ifstream fShaderFile; + // ensure ifstream objects can throw exceptions: + // vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit); + // fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit); + try + { + // open files + + vShaderFile.open(vertexPath); + + if (!vShaderFile.is_open()) + std::cout << "ERROR:SHADER::VERTEX::FILE_NOT_READ" << std::endl; + + fShaderFile.open(fragmentPath); + + if (!fShaderFile.is_open()) + std::cout << "ERROR:SHADER::FRAGMENT::FILE_NOT_READ" << std::endl; + + + + std::stringstream vShaderStream, fShaderStream; + // read file's buffer contents into streams + vShaderStream << vShaderFile.rdbuf(); + fShaderStream << fShaderFile.rdbuf(); + // close file handlers + vShaderFile.close(); + fShaderFile.close(); + // convert stream into string + vertexCode = vShaderStream.str(); + fragmentCode = fShaderStream.str(); + } + catch (std::ifstream::failure& e) + { + std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ: " << e.what() << std::endl; + } + + std::cout << fragmentCode << std::endl; + + const char* vShaderCode = vertexCode.c_str(); + const char * fShaderCode = fragmentCode.c_str(); + // 2. compile shaders + unsigned int vertex, fragment; + // vertex shader + vertex = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex, 1, &vShaderCode, NULL); + glCompileShader(vertex); + checkCompileErrors(vertex, "VERTEX"); + // fragment Shader + fragment = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment, 1, &fShaderCode, NULL); + glCompileShader(fragment); + checkCompileErrors(fragment, "FRAGMENT"); + // shader Program + ID = glCreateProgram(); + glAttachShader(ID, vertex); + glAttachShader(ID, fragment); + glLinkProgram(ID); + checkCompileErrors(ID, "PROGRAM"); + // delete the shaders as they're linked into our program now and no longer necessary + glDeleteShader(vertex); + glDeleteShader(fragment); + } + // activate the shader + // ------------------------------------------------------------------------ + void use() + { + glUseProgram(ID); + } + // utility uniform functions + // ------------------------------------------------------------------------ + void setBool(const std::string &name, bool value) const + { + glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value); + } + // ------------------------------------------------------------------------ + void setInt(const std::string &name, int value) const + { + glUniform1i(glGetUniformLocation(ID, name.c_str()), value); + } + // ------------------------------------------------------------------------ + void setFloat(const std::string &name, float value) const + { + glUniform1f(glGetUniformLocation(ID, name.c_str()), value); + } + void setVec2(const std::string &name, const glm::vec2 &value) const + { + glUniform2fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]); + } + void setVec2(const std::string &name, float x, float y) const + { + glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y); + } + // ------------------------------------------------------------------------ + void setVec3(const std::string &name, const glm::vec3 &value) const + { + glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]); + } + void setVec3(const std::string &name, float x, float y, float z) const + { + glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z); + } + // ------------------------------------------------------------------------ + void setVec4(const std::string &name, const glm::vec4 &value) const + { + glUniform4fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]); + } + void setVec4(const std::string &name, float x, float y, float z, float w) const + { + glUniform4f(glGetUniformLocation(ID, name.c_str()), x, y, z, w); + } + // ------------------------------------------------------------------------ + void setMat2(const std::string &name, const glm::mat2 &mat) const + { + glUniformMatrix2fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]); + } + // ------------------------------------------------------------------------ + void setMat3(const std::string &name, const glm::mat3 &mat) const + { + glUniformMatrix3fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]); + } + // ------------------------------------------------------------------------ + void setMat4(const std::string &name, const glm::mat4 &mat) const + { + glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]); + } + +private: + // utility function for checking shader compilation/linking errors. + // ------------------------------------------------------------------------ + void checkCompileErrors(unsigned int shader, std::string type) + { + int success; + char infoLog[1024]; + if (type != "PROGRAM") + { + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) + { + glGetShaderInfoLog(shader, 1024, NULL, infoLog); + std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; + } + } + else + { + glGetProgramiv(shader, GL_LINK_STATUS, &success); + if (!success) + { + glGetProgramInfoLog(shader, 1024, NULL, infoLog); + std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; + } + } + } +}; +#endif + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..1fe2fa8 --- /dev/null +++ b/main.cpp @@ -0,0 +1,393 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "Object3D.h" +#include "Shader.h" +#include "Camera.h" + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void processInput(GLFWwindow *window); + +// settings +unsigned int SCR_WIDTH = 800; +unsigned int SCR_HEIGHT = 600; + +bool first_mouse = true; +bool mouse_lock = true; +float last_x = 400, last_y = 300; + +// Flipped Stuff +const float flip_cooldown = 1.0f; // In seconds +float last_flip = 0.0f; +int flipped = 1; // 1 for normal, -1 for flipped +bool flipping = false; +glm::vec3 flip_target = glm::vec3(0.0f); + +// Various other settings +const float camera_speed = 0.05f; +const float look_sensitivity = 0.1f; +const float Z_FAR = 1000.0f; +const float Z_NEAR = 0.1f; +const bool WIREFRAME_MODE = !true; +const float TARGET_FPS = 60.0f; +const float FOV = 90.0f; +float sleep_time = 15.0f; + +glm::mat4 projection = glm::mat4(1.0f); + +float yaw, pitch = 0; + +// Camera +Camera camera = Camera(camera_speed); + + +// Lighting +glm::vec3 lightPos(1.2f, 1.0f, 2.0f); +glm::vec3 lightColor(0.557, 0.482, 0.6); + +glm::vec3 objectColor(0.671f, 0.671f, 0.671f); + +glm::vec3 cubePositions[] = { + glm::vec3( 0.0f, 0.0f, 0.0f), + glm::vec3( 2.0f, 5.0f, -15.0f), + glm::vec3(-1.5f, -2.2f, -2.5f), + glm::vec3(-3.8f, -2.0f, -12.3f), + glm::vec3 (2.4f, -0.4f, -3.5f), + glm::vec3(-1.7f, 3.0f, -7.5f), + glm::vec3( 1.3f, -2.0f, -2.5f), + glm::vec3( 1.5f, 2.0f, -2.5f), + glm::vec3( 1.5f, 0.2f, -1.5f), + glm::vec3(-1.3f, 1.0f, -1.5f) +}; + +// Mouse Look +void mouse_callback(GLFWwindow * window, double xpos, double ypos) +{ + if (flipping) + return; + + float x_offset = xpos - last_x; + float y_offset = ypos - last_y; + last_x = xpos; + last_y = ypos; + + if (first_mouse) + { + first_mouse = false; + return; + } + + x_offset *= look_sensitivity; + y_offset *= look_sensitivity; + + yaw += x_offset * flipped; + pitch += y_offset * flipped; + + if (pitch > 89.0f) + pitch = 89.0f; + if (pitch < -89.0f) + pitch = -89.0f; + + glm::vec3 direction; + direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch)); + direction.y = -sin(glm::radians(pitch)); + direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch)); + camera.target = glm::normalize(direction); +} + +int main() +{ + // glfw: initialize and configure + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + + // glfw window creation + GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Test", NULL, NULL); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwMakeContextCurrent(window); + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + + // glad: load all OpenGL function pointers + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD" << std::endl; + return -1; + } + + // ------ Basic Lighting ------ + glm::vec3 light_direction(0.0f, 1.0f, 0.0f); + + // ------ Setup Shader ------ + Shader shader("shaders/shader.vert", "shaders/shader.frag"); + + // ------ World Objects ------ + std::vector world_objects; + Object3D cube = Object3D("objs/sphere.obj"); + + world_objects.push_back(cube); + + // ------ Buffer Setup ------ + unsigned int VBO, VAO, EBO; + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + glGenBuffers(1, &EBO); + + // Move VBO into GPU memory + glBindBuffer(GL_ARRAY_BUFFER, VBO); + + // Get VBO buffer from 3D Object (combine vertices and normals) + std::vector object_VBO_buffer = cube.getVBOBuffer(); + + // for (int i = 0; i < object_VBO_buffer.size(); i++) + // std::cout << object_VBO_buffer[i] << std::endl; + + // std::cout << "VBO buffer" << std::endl; + // for (int i = 0; i < cube.VBO_buffer.size(); i++) + // std::cout << cube.VBO_buffer[i] << std::endl; + + // std::cout << "EBO buffer " << cube.EBO_buffer.size() << std::endl; + // for (int i = 0; i < cube.EBO_buffer.size(); i++) + // std::cout << cube.EBO_buffer[i] + 1 << std::endl; + + // std::cout << "Faces in cube obj" << std::endl; + // for (int i = 0; i < cube.indices.size(); i++) + // std::cout << cube.indices[i] + 1 << std::endl; + + // std::cout << "vertices" << std::endl; + // for (int i = 0; i < cube.vertices.size(); i++) + // std::cout << i << " " << cube.vertices[i] << std::endl; + + glBufferData(GL_ARRAY_BUFFER, cube.VBO_buffer.size() * sizeof(float), &cube.VBO_buffer.front(), GL_STATIC_DRAW); + + glBindVertexArray(VAO); + + // Tell GPU how to read the memory + + // Position Attribute + glVertexAttribPointer(0, 3, GL_FLOAT, GL_TRUE, 6 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + // Normal Attribute + glVertexAttribPointer(1, 3, GL_FLOAT, GL_TRUE, 6 * sizeof(float), (void*)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + + // Setup EBO + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, cube.EBO_buffer.size() * sizeof(unsigned int), &cube.EBO_buffer.front(), GL_STATIC_DRAW); + + + // Color Attribute + // glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); + // glEnableVertexAttribArray(1); + + + // ------ OpenGL Settings ------ + if (WIREFRAME_MODE) + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + glEnable(GL_DEPTH_TEST); // Z-Buffer + + double start_time = glfwGetTime(); + + // Setup mouse look + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + glfwSetCursorPosCallback(window, mouse_callback); + + + int frame_count = 0; + projection = glm::perspective(glm::radians(FOV), (float)SCR_WIDTH / (float)SCR_HEIGHT, Z_NEAR, Z_FAR); // This doesn't need to be recalculated every frame + + // ------ Rendering Loop ------ + while (!glfwWindowShouldClose(window)) + { + // input + processInput(window); + + // ------ SETUP Matrices ------ + shader.use(); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Z-Buffer + + + // Camera Rotate around origin + // const float radius = 10.0f; + // camera.position.x = sin(glfwGetTime()) * radius; + // camera.position.z = cos(glfwGetTime()) * radius; + + glm::mat4 view = camera.getView(); + + shader.setMat4("projection", projection); + shader.setMat4("view", view); + + // Fragment shader things + shader.setVec3("lightPos", lightPos); + shader.setVec3("viewPos", camera.position); + shader.setVec3("lightColor", lightColor); + shader.setVec3("objectColor", objectColor); + + // ------ Render ------ + glBindVertexArray(VAO); + // glDrawArrays(GL_TRIANGLES, 0, 3); + + // ---- Rotate Model ---- + // glm::mat4 model = glm::mat4(1.0f); + // model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); + // float angle = 20.0f * glfwGetTime(); + // model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.5f, 0.0f)); + // shader.setMat4("model", model); + + for (unsigned int i = 0; i < 10; i++) + { + // calculate the model matrix for each object and pass it to shader before drawing + glm::mat4 model = glm::mat4(1.0f); + model = glm::translate(model, cubePositions[i]); + float angle = 20.0f * i; + model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f)); + shader.setMat4("model", model); + + // glDrawArrays(GL_TRIANGLES, 0, 36); + glDrawElements(GL_TRIANGLES, cube.EBO_buffer.size(), GL_UNSIGNED_INT, nullptr); + } + + + // glDrawElements(GL_TRIANGLES, cube.EBO_buffer.size(), GL_UNSIGNED_INT, nullptr); + + frame_count++; + double end_time = glfwGetTime(); + double delta_time = end_time - start_time; + + // Flip Animation attempt + if (flipping && delta_time > 0.1f) + { + float flip_time = last_flip - end_time; + glm::mat4 rotate = glm::mat4(1.0f); + float angle = 18.0f * flip_time; + rotate = glm::rotate(rotate, glm::radians(angle), glm::vec3(0.0f, 0.0f, 1.0f)); + camera.worldUp = glm::vec3(rotate * glm::vec4(camera.worldUp, 1.0f)); + + if (flip_time > 0.5f) + flipped *= -1; + if (camera.worldUp.y >= flip_target.y) + { + flipping = false; + camera.worldUp.y = flip_target.y; + } + } + + // ------ FPS output ------ + if (delta_time >= 1) + { + const double FPS = frame_count / (delta_time); + std::cout << "FPS: " << FPS << std::endl; + + frame_count = 0; + start_time = glfwGetTime(); + + // ---- Dynamic FPS ---- + if (FPS > TARGET_FPS) + sleep_time += 5.0f; + if (FPS < TARGET_FPS) + sleep_time -= 5.0f; + } + + + Sleep(sleep_time); + + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // glfw: terminate, clearing all previously allocated GLFW resources. + glfwTerminate(); + return 0; +} + +// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly +void processInput(GLFWwindow *window) +{ + if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { + // if (mouse_lock) { + // glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + // mouse_lock = false; + // } else { + // glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + // mouse_lock = true; + // } + glfwSetWindowShouldClose(window, true); + } + + // ---- Movement ---- + // Forward / Backward + if(glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + camera.position += camera.speed * camera.target; + if(glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + camera.position -= camera.speed * camera.target; + + // Horizontal + if(glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + camera.position -= glm::normalize(glm::cross(camera.target, camera.worldUp)) * camera.speed; + if(glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + camera.position += glm::normalize(glm::cross(camera.target, camera.worldUp)) * camera.speed; + + // Vertical + if(glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) + camera.position -= glm::normalize(glm::cross(camera.target, glm::normalize(glm::cross(camera.target, camera.worldUp)))) * camera.speed * 0.5f; + if(glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) + camera.position += glm::normalize(glm::cross(camera.target, glm::normalize(glm::cross(camera.target, camera.worldUp)))) * camera.speed * 0.5f; + + // Sprint + if(glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) + camera.speed = camera_speed; + if(glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_RELEASE) + camera.speed = camera_speed * 5; + + // World Flip + if(glfwGetKey(window, GLFW_KEY_F) == GLFW_PRESS) + { + float current_time = glfwGetTime(); + if (current_time - last_flip > flip_cooldown && !flipping) + { + // flip_target = camera.worldUp; + // flip_target *= -1; + + camera.worldUp *= -1; + last_flip = current_time; + flipped *= -1; + // flipping = true; + } + } +} + +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); + SCR_WIDTH = width; + SCR_HEIGHT = height; + + // Perspective needs to be recalculated on window size change + projection = glm::perspective(glm::radians(FOV), (float)SCR_WIDTH / (float)SCR_HEIGHT, Z_NEAR, Z_FAR); +} + diff --git a/shaders/shader.frag b/shaders/shader.frag new file mode 100644 index 0000000..59ff15a --- /dev/null +++ b/shaders/shader.frag @@ -0,0 +1,50 @@ +#version 330 core +out vec4 FragColor; + + +uniform vec3 lightColor; +uniform vec3 objectColor; +uniform vec3 viewPos; + +in vec3 FragPos; +in vec3 Normal; +in vec3 LightPos; + +const float ambientStrength = 0.2; +const float shininess = 32.0; +const float screenGamma = 2.2; // Assume the monitor is calibrated to the sRGB color space + +void main() +{ + // FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f); + + vec3 lightDir = LightPos - FragPos; + float distance = length(lightDir); + vec3 norm = normalize(Normal); + vec3 ambient = ambientStrength * lightColor; + + distance = distance * distance; + lightDir = normalize(lightDir); + + float lambertian = max(dot(norm, lightDir), 0.0); + vec3 diffuse = lambertian * lightColor; + float specular = 0.0; + + if (lambertian > 0.0) + { + vec3 viewDir = normalize(-FragPos); + + vec3 halfDir = normalize(lightDir + viewDir); + float specAngle = max(dot(halfDir, norm), 0.0); + specular = pow(specAngle, shininess); + } + + vec3 colorLinear = (ambient + diffuse + specular) * objectColor; + + // vec3 colorGammaCorrected = pow(colorLinear, vec3(1.0 / screenGamma)); + + FragColor = vec4(colorLinear, 1.0); + + // vec3 norm = normalize(Normal); +} + diff --git a/shaders/shader.vert b/shaders/shader.vert new file mode 100644 index 0000000..c818fab --- /dev/null +++ b/shaders/shader.vert @@ -0,0 +1,22 @@ +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; +//layout (location = 1) in vec3 aColor; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; +uniform vec3 lightPos; + +out vec3 FragPos; +out vec3 Normal; +out vec3 LightPos; + +void main() +{ + gl_Position = projection * view * model * vec4(aPos, 1.0); + FragPos = vec3(view * model * vec4(aPos, 1.0)); + Normal = mat3(transpose(inverse(view * model))) * aNormal; + LightPos = vec3(view * vec4(lightPos, 1.0)); +} +