commit 46df79ca2a58149667524be4f16e71df52b7fa1d Author: Jackson H Date: Tue Jun 4 10:31:34 2024 -0500 init 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)); +} +