#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); // Jump const float jump_cooldown = 1.0f; float last_jump = 0.0f; // Various other settings const float camera_speed = 1.0f; const float max_camera_speed = 2.0f; 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; bool W_pressed, S_pressed, A_pressed, D_pressed = false; glm::mat4 projection = glm::mat4(1.0f); float yaw, pitch = 0; // Camera Camera camera = Camera(camera_speed, glm::vec3(0.0f, 0.0f, 115.0f)); // Lighting glm::vec3 lightPos(1.2f, 1.0f, 2.0f); glm::vec3 lightColor(1.0f, 1.0f, 0.949f); glm::vec3 objectColor(0.0f, 1.0f, 0.0f); float planets[][2] = { {140.0f, 1.0f} }; // 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"); Shader sun_shader("shaders/sun.vert", "shaders/sun.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(); 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(); double move_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 ------ glm::mat4 view = camera.getView(); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Z-Buffer // Orbitting sun thing? float t = glfwGetTime() * 10.0f; float radius = 15.0f; glm::mat4 model = glm::mat4(1.0f); model = glm::scale(model, glm::vec3(109)); // The sun should be BIG glm::vec3 sun_pos = glm::vec3(0.0f, 0.0f, 0.0f); model = glm::translate(model, sun_pos); sun_shader.use(); sun_shader.setMat4("model", model); sun_shader.setMat4("view", view); sun_shader.setMat4("projection", projection); sun_shader.setVec3("lightColor", lightColor); glDrawElements(GL_TRIANGLES, cube.EBO_buffer.size(), GL_UNSIGNED_INT, nullptr); // Switch to normal shader shader.use(); shader.setMat4("projection", projection); shader.setMat4("view", view); // Fragment shader things shader.setFloat("light_power", 40.0f); shader.setVec3("lightPos", sun_pos); 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 < sizeof(planets) / sizeof(float[2]); i++) { // calculate the model matrix for each object and pass it to shader before drawing glm::mat4 model = glm::mat4(1.0f); float radius = planets[i][0]; model = glm::scale(model, glm::vec3(planets[i][1])); float x = sin(glm::radians(0.0f)) * radius; float z = cos(glm::radians(0.0f)) * radius; model = glm::translate(model, glm::vec3(x, 0.0f, z)); 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; } } float time_since_last_move = end_time - move_time; if (time_since_last_move >= 0.017) { camera.move(time_since_last_move); move_time = end_time; } // ------ 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) { float current_time = glfwGetTime(); 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.forward_velocity = camera.speed * glm::vec3(camera.target.x, 0.0f, camera.target.z); W_pressed = true; } if(glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { camera.forward_velocity = -camera.speed * glm::vec3(camera.target.x, 0.0f, camera.target.z); S_pressed = true; } if(glfwGetKey(window, GLFW_KEY_S) == GLFW_RELEASE && S_pressed) { camera.forward_velocity = glm::vec3(0.0f); S_pressed = false; } if(glfwGetKey(window, GLFW_KEY_W) == GLFW_RELEASE && W_pressed) { camera.forward_velocity = glm::vec3(0.0f); W_pressed = false; } // Horizontal if(glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { camera.horizontal_velocity = -glm::normalize(glm::cross(glm::vec3(camera.target.x, 0.0f, camera.target.z), camera.worldUp)) * camera.speed; A_pressed = true; } if(glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { camera.horizontal_velocity = glm::normalize(glm::cross(glm::vec3(camera.target.x, 0.0f, camera.target.z), camera.worldUp)) * camera.speed; D_pressed = true; } if(glfwGetKey(window, GLFW_KEY_A) == GLFW_RELEASE && A_pressed) { camera.horizontal_velocity = glm::vec3(0.0f); A_pressed = false; } if(glfwGetKey(window, GLFW_KEY_D) == GLFW_RELEASE && D_pressed) { camera.horizontal_velocity = glm::vec3(0.0f); D_pressed = false; } // Jump if(glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) // camera.velocity -= glm::normalize(glm::cross(camera.target, glm::normalize(glm::cross(camera.target, camera.worldUp)))) * camera.speed * 0.5f; { if (current_time - last_jump > jump_cooldown && camera.position.y == 0) { camera.vertical_velocity.y += 10.0f; last_jump = current_time; } } // Sprint if(glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) camera.speed = camera_speed * 7.5; if(glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_RELEASE) camera.speed = camera_speed * 20; // World Flip if(glfwGetKey(window, GLFW_KEY_F) == GLFW_PRESS) { 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); }