diff --git a/src/main.cc b/src/main.cc index fe0bed5..d075ba7 100644 --- a/src/main.cc +++ b/src/main.cc @@ -10,14 +10,6 @@ #include "utilities.hh" #include "image.hh" -void framebuffer_size_callback(GLFWwindow* window, int width, int height); -void process_input(GLFWwindow* window); -void key_pressed(GLFWwindow *window, int key, int scancode, int action, int mods); -int check_shader_compilation_status(unsigned int shaderId); - -bool wireframe; -bool rectangleToggled; - struct GlRenderObjects { unsigned int vertexBufferObject; unsigned int vertexArrayObject; @@ -30,6 +22,290 @@ struct GlRenderObjects { } }; +enum state { + CUBE, + RECTANGLE, + TRIANGLE +}; + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void process_input(GLFWwindow* window); +void key_pressed(GLFWwindow *window, int key, int scancode, int action, int mods); +int check_shader_compilation_status(unsigned int shaderId); +void createTexture(uint* textureInt, const char* path, GLenum imageType, GLenum minFilter, GLenum magFilter); +GlRenderObjects cube(); +GlRenderObjects triangle(); +GlRenderObjects rectangle(); + +bool wireframe; +bool rectangleToggled; +state currentState = CUBE; + +int main() { + glfwInit(); + +// These brackets are a RAII (Resource Acquisition Is Initialization) pattern, +// which means these brackets are its own "scope" which means stack allocated memory +// and heap allocated memory wrapped in a smart pointer like unique_ptr will be cleaned +// up after you go out of scope. +{ + // Set OpenGL version to 3.3. + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + + // Set OpenGL to use the Core profile. + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + // Create a OpenGL window with the 800x600 resolution and with the title "OpenGL Sussy Window". + GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Sussy Window", nullptr, nullptr); + + // Fail if the window is failed to create. + if (window == nullptr) { + std::cout << "Failed to create GLFW window." << std::endl; + glfwTerminate(); + return -1; + } + + glfwMakeContextCurrent(window); + + // Tell OpenGL the size of the rendering window. We're telling it right now that its 800x600. + glViewport(0, 0, 800, 600); + + // Register a callback for window size changes. + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + + std::string currentPath = utilities::getCurrentPath(); + + // Create a shader class. + Shader shader(std::format("{}/vertex.glsl", currentPath).c_str(), std::format("{}/fragment.glsl", currentPath).c_str()); + + GlRenderObjects cubeRenderObjs = cube(); + GlRenderObjects triangleRenderObjs = triangle(); + GlRenderObjects rectangleRenderObjs = rectangle(); + + unsigned int texture1, texture2; + + createTexture(&texture1, "container.jpg", GL_RGB, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); + createTexture(&texture2, "awesomeface.png", GL_RGBA, GL_LINEAR, GL_LINEAR); + + // activate the shader + shader.activate(); + + // tell the shader what texture unit the textures are on + shader.setInt("texture1", 0); + shader.setInt("texture2", 1); + + // glm::mat4 trans = glm::mat4(1.0f); + // trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0)); + // trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5)); + + // Create a render loop, which keeps the program open until glfw tells the loop that the window should close. + while (!glfwWindowShouldClose(window)) { + // Process input. + process_input(window); + // Set a callback to trigger whenever a key is pressed. + glfwSetKeyCallback(window, key_pressed); + + // Set the GL state to use this color when clearing the screen. + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + // Clear the screen and use the color from the state. + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // assign texture1 to the GL_TEXTURE0 texture unit. + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture1); + + // assign texture1 to the GL_TEXTURE1 texture unit. + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, texture2); + + shader.activate(); + + // the model matrix. this rotates the plane on the x axis by -55 degrees. + glm::mat4 model(1.0f); + model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(0.5f, 1.0f, 0.0f)); + + // the view matrix. this moves the "camera" back by 3 units. + glm::mat4 view(1.0f); + view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f)); + + // the projection matrix. the fov is 45 degrees. the aspect is 800/600 (4:3). the near plane is 0.1 units. the far plane is 100 units. + glm::mat4 projection; + projection = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f); + + // create the identity matrix + glm::mat4 trans(1); + // translate it by this vector3 + trans = glm::translate(trans, glm::vec3(0.5f, -0.5f, 0.0f)); + // rotate by the z axis with the angle being controlled by time + trans = glm::rotate(trans, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f)); + + shader.setMat4("model", model); + shader.setMat4("view", view); + shader.setMat4("projection", projection); + + switch (currentState) { + case CUBE: + glEnable(GL_DEPTH_TEST); + glBindVertexArray(cubeRenderObjs.vertexArrayObject); + glDrawArrays(GL_TRIANGLES, 0, 36); + glBindVertexArray(0); + glDisable(GL_DEPTH_TEST); + break; + case RECTANGLE: + glBindVertexArray(rectangleRenderObjs.vertexArrayObject); + // Draw 6 elements (indices) that are unsigned ints. + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + glBindVertexArray(0); + break; + case TRIANGLE: + // Bind the state to use the vertexArrayObject object so OpenGL knows what to do with the verticies. + glBindVertexArray(triangleRenderObjs.vertexArrayObject); + // Draw the verticies with the first index being 0 and the size of the vertex being 3 floats. + glDrawArrays(GL_TRIANGLES, 0, 3); + glBindVertexArray(0); + break; + } + + // This swaps completely drawn frames from the "second buffer" to the front one which is displayed on the screen. + glfwSwapBuffers(window); + // Check if any events are triggered. + glfwPollEvents(); + } +} + + glfwTerminate(); + return 0; +} + +void framebuffer_size_callback(GLFWwindow* window, int width, int height) { + // Tell OpenGL the size of the rendering window. + glViewport(0, 0, width, height); +} + +void process_input(GLFWwindow* window) { + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { + glfwSetWindowShouldClose(window, true); + } +} + +void key_pressed(GLFWwindow *window, int key, int scancode, int action, int mods) { + // If the W key is pressed, toggle wireframe mode. + if (key == GLFW_KEY_W && action == GLFW_PRESS) { + if (!wireframe) { + // Render polygons in wireframe mode. + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + wireframe = true; + } else { + // Render polygons in fill mode. + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + wireframe = false; + } + } + + + if (key == GLFW_KEY_R && action == GLFW_PRESS) { + currentState = RECTANGLE; + } else if (key == GLFW_KEY_T && action == GLFW_PRESS) { + currentState = TRIANGLE; + } else if (key == GLFW_KEY_C && action == GLFW_PRESS) { + currentState = CUBE; + } +} + +int check_shader_compilation_status(unsigned int shaderId) { + int success; + glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success); + return success; +} + +void createTexture(uint* textureInt, const char* path, GLenum imageType, GLenum minFilter, GLenum magFilter) { + // Create a texture ID and assign it to the textureInt pointer. + glGenTextures(1, textureInt); + // Bind that texture to the GL_TEXTURE_2D state. + glBindTexture(GL_TEXTURE_2D, *textureInt); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter); + + Image texture(utilities::getCurrentPath(path)); + if (texture.data) { + // Read the texture data and assign it to the GL texture. + glTexImage2D(GL_TEXTURE_2D, 0, imageType, texture.width, texture.height, 0, imageType, GL_UNSIGNED_BYTE, texture.data); + // Generate a mipmap of the texture data. + glGenerateMipmap(GL_TEXTURE_2D); + } else { + std::cout << "Failed to load texture." << std::endl; + } +} + +GlRenderObjects cube() { + float vertices[] = { + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, + + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, + + -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, + + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, + + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, + + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f + }; + + unsigned int vertexArrayObject, vertexBufferObject; + glGenVertexArrays(1, &vertexArrayObject); + glGenBuffers(1, &vertexBufferObject); + glBindVertexArray(vertexArrayObject); + + glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); + glEnableVertexAttribArray(2); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + return {vertexBufferObject, vertexArrayObject, 0}; +} + GlRenderObjects triangle() { // An array of verticies containing data for a triangle. float vertices[] = { @@ -72,7 +348,7 @@ GlRenderObjects triangle() { glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); - return {vertexArrayObject, vertexArrayObject, 0}; + return {vertexBufferObject, vertexArrayObject, 0}; } GlRenderObjects rectangle() { @@ -128,188 +404,3 @@ GlRenderObjects rectangle() { return {vertexBufferObject, vertexArrayObject, elementBufferObject}; } - -void createTexture(uint* textureInt, const char* path, GLenum imageType, GLenum minFilter, GLenum magFilter) { - // Create a texture ID and assign it to the textureInt pointer. - glGenTextures(1, textureInt); - // Bind that texture to the GL_TEXTURE_2D state. - glBindTexture(GL_TEXTURE_2D, *textureInt); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter); - - Image texture(utilities::getCurrentPath(path)); - if (texture.data) { - // Read the texture data and assign it to the GL texture. - glTexImage2D(GL_TEXTURE_2D, 0, imageType, texture.width, texture.height, 0, imageType, GL_UNSIGNED_BYTE, texture.data); - // Generate a mipmap of the texture data. - glGenerateMipmap(GL_TEXTURE_2D); - } else { - std::cout << "Failed to load texture." << std::endl; - } -} - -int main() { - glfwInit(); - -// These brackets are a RAII (Resource Acquisition Is Initialization) pattern, -// which means these brackets are its own "scope" which means stack allocated memory -// and heap allocated memory wrapped in a smart pointer like unique_ptr will be cleaned -// up after you go out of scope. -{ - // Set OpenGL version to 3.3. - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - - // Set OpenGL to use the Core profile. - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - - // Create a OpenGL window with the 800x600 resolution and with the title "OpenGL Sussy Window". - GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Sussy Window", nullptr, nullptr); - - // Fail if the window is failed to create. - if (window == nullptr) { - std::cout << "Failed to create GLFW window." << std::endl; - glfwTerminate(); - return -1; - } - - glfwMakeContextCurrent(window); - - // Tell OpenGL the size of the rendering window. We're telling it right now that its 800x600. - glViewport(0, 0, 800, 600); - - // Register a callback for window size changes. - glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); - - std::string currentPath = utilities::getCurrentPath(); - - // Create a shader class. - Shader shader(std::format("{}/vertex.glsl", currentPath).c_str(), std::format("{}/fragment.glsl", currentPath).c_str()); - - GlRenderObjects triangleRenderObjs = triangle(); - GlRenderObjects rectangleRenderObjs = rectangle(); - - unsigned int texture1, texture2; - - createTexture(&texture1, "container.jpg", GL_RGB, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); - createTexture(&texture2, "awesomeface.png", GL_RGBA, GL_LINEAR, GL_LINEAR); - - // activate the shader - shader.activate(); - - // tell the shader what texture unit the textures are on - shader.setInt("texture1", 0); - shader.setInt("texture2", 1); - - // glm::mat4 trans = glm::mat4(1.0f); - // trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0)); - // trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5)); - - // Create a render loop, which keeps the program open until glfw tells the loop that the window should close. - while (!glfwWindowShouldClose(window)) { - // Process input. - process_input(window); - // Set a callback to trigger whenever a key is pressed. - glfwSetKeyCallback(window, key_pressed); - - // Set the GL state to use this color when clearing the screen. - glClearColor(0.2f, 0.3f, 0.3f, 1.0f); - // Clear the screen and use the color from the state. - glClear(GL_COLOR_BUFFER_BIT); - - // assign texture1 to the GL_TEXTURE0 texture unit. - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texture1); - - // assign texture1 to the GL_TEXTURE1 texture unit. - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, texture2); - - shader.activate(); - - // the model matrix. this rotates the plane on the x axis by -55 degrees. - glm::mat4 model(1.0f); - model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f)); - - // the view matrix. this moves the "camera" back by 3 units. - glm::mat4 view(1.0f); - view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f)); - - // the projection matrix. the fov is 45 degrees. the aspect is 800/600 (4:3). the near plane is 0.1 units. the far plane is 100 units. - glm::mat4 projection; - projection = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f); - - // create the identity matrix - glm::mat4 trans(1); - // translate it by this vector3 - trans = glm::translate(trans, glm::vec3(0.5f, -0.5f, 0.0f)); - // rotate by the z axis with the angle being controlled by time - trans = glm::rotate(trans, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f)); - - shader.setMat4("model", model); - shader.setMat4("view", view); - shader.setMat4("projection", projection); - - if (!rectangleToggled) { - // Bind the state to use the vertexArrayObject object so OpenGL knows what to do with the verticies. - glBindVertexArray(triangleRenderObjs.vertexArrayObject); - // Draw the verticies with the first index being 0 and the size of the vertex being 3 floats. - glDrawArrays(GL_TRIANGLES, 0, 3); - glBindVertexArray(0); - } else { - glBindVertexArray(rectangleRenderObjs.vertexArrayObject); - // Draw 6 elements (indices) that are unsigned ints. - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); - glBindVertexArray(0); - } - - // This swaps completely drawn frames from the "second buffer" to the front one which is displayed on the screen. - glfwSwapBuffers(window); - // Check if any events are triggered. - glfwPollEvents(); - } -} - - glfwTerminate(); - return 0; -} - -void framebuffer_size_callback(GLFWwindow* window, int width, int height) { - // Tell OpenGL the size of the rendering window. - glViewport(0, 0, width, height); -} - -void process_input(GLFWwindow* window) { - if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { - glfwSetWindowShouldClose(window, true); - } -} - -int check_shader_compilation_status(unsigned int shaderId) { - int success; - glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success); - return success; -} - -void key_pressed(GLFWwindow *window, int key, int scancode, int action, int mods) { - // If the W key is pressed, toggle wireframe mode. - if (key == GLFW_KEY_W && action == GLFW_PRESS) { - if (!wireframe) { - // Render polygons in wireframe mode. - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - wireframe = true; - } else { - // Render polygons in fill mode. - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - wireframe = false; - } - } - - // Flip a boolean indicating the rectangle should be rendered. - if (key == GLFW_KEY_R && action == GLFW_PRESS) { - rectangleToggled = !rectangleToggled; - } -}