#include #include #include #include #include #include #include #include #include #define GLM_ENABLE_EXPERIMENTAL #include #include #define IMGUI_DEFINE_MATH_OPERATORS // ImGui #include #include #include #include #include #include #include #include #include #include #include #include "shader.h" constexpr int WIDTH = 1366; constexpr int HEIGHT = 768; static void glfwErrorCallback(int e, const char *description) { clog_log(CLOG_LEVEL_ERROR, "GLFW Error %d: %s", e, description); } namespace fred { static bool loadModel(const char *path, std::vector &indices, std::vector &vertices, std::vector &uvs, std::vector &normals) { clog_log(CLOG_LEVEL_DEBUG, "Loading model: %s", path); Assimp::Importer importer; const aiScene *scene = importer.ReadFile( path, aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType); if (!scene) { clog_log(CLOG_LEVEL_ERROR, "%s", importer.GetErrorString()); return false; } const aiMesh *mesh = scene->mMeshes[0]; vertices.reserve(mesh->mNumVertices); for (unsigned int i = 0; i < mesh->mNumVertices; i++) { aiVector3D pos = mesh->mVertices[i]; vertices.push_back(glm::vec3(pos.x, pos.y, pos.z)); } uvs.reserve(mesh->mNumVertices); for (unsigned int i = 0; i < mesh->mNumVertices; i++) { aiVector3D UVW = mesh->mTextureCoords[0][i]; // Multiple UVs? Prepsterous! uvs.push_back(glm::vec2(UVW.x, UVW.y)); } normals.reserve(mesh->mNumVertices); for (unsigned int i = 0; i < mesh->mNumFaces; i++) { indices.push_back(mesh->mFaces[i].mIndices[0]); indices.push_back(mesh->mFaces[i].mIndices[1]); indices.push_back(mesh->mFaces[i].mIndices[2]); } return true; } GLuint loadTexture(const char *path) { clog_log(CLOG_LEVEL_DEBUG, "Loading texture: %s", path); GLuint texture = SOIL_load_OGL_texture( path, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_MIPMAPS | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT | SOIL_FLAG_INVERT_Y); if (texture == 0) { clog_log(CLOG_LEVEL_WARN, "Texture failed to load"); return 0; } return texture; } class Model { public: std::vector indices; GLuint vertexBuffer; GLuint uvBuffer; GLuint normalBuffer; GLuint elementBuffer; Model(std::string modelPath) { std::vector indexed_vertices; std::vector indexed_uvs; std::vector indexed_normals; loadModel(modelPath.c_str(), indices, indexed_vertices, indexed_uvs, indexed_normals); glGenBuffers(1, &vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); glBufferData(GL_ARRAY_BUFFER, indexed_vertices.size() * sizeof(glm::vec3), &indexed_vertices[0], GL_STATIC_DRAW); glGenBuffers(1, &uvBuffer); glBindBuffer(GL_ARRAY_BUFFER, uvBuffer); glBufferData(GL_ARRAY_BUFFER, indexed_uvs.size() * sizeof(glm::vec2), &indexed_uvs[0], GL_STATIC_DRAW); glGenBuffers(1, &normalBuffer); glBindBuffer(GL_ARRAY_BUFFER, normalBuffer); glBufferData(GL_ARRAY_BUFFER, indexed_normals.size() * sizeof(glm::vec3), &indexed_normals[0], GL_STATIC_DRAW); glGenBuffers(1, &elementBuffer); glBindBuffer(GL_ARRAY_BUFFER, elementBuffer); glBufferData(GL_ARRAY_BUFFER, indices.size() * sizeof(unsigned short), &indices[0], GL_STATIC_DRAW); } ~Model() { // glDeleteBuffers(4, vertexBuffer); // Chances that these are contiguous in memory is zero glDeleteBuffers(1, &vertexBuffer); glDeleteBuffers(1, &uvBuffer); glDeleteBuffers(1, &normalBuffer); glDeleteBuffers(1, &elementBuffer); } }; class Texture { public: GLuint texture; Texture(std::string texturePath) { texture = loadTexture(texturePath.c_str()); } ~Texture() { glDeleteTextures(1, &texture); } }; class Shader { public: GLuint shaderProgram; Shader(std::string vertPath, std::string fragPath) { shaderProgram = loadShaders(vertPath.c_str(), fragPath.c_str()); } ~Shader() { glDeleteProgram(shaderProgram); } }; class Asset { public: std::vector *indices; GLuint *vertexBuffer; GLuint *uvBuffer; GLuint *normalBuffer; GLuint *elementBuffer; GLuint matrixID; GLuint textureID; glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f); glm::quat rotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); // https://en.wikipedia.org/wiki/Quaternion glm::vec3 scaling = glm::vec3(1.0f, 1.0f, 1.0f); GLuint *modelTexture; GLuint *shaderProgram; Asset(Model &model, Texture &texture, Shader &shader) { indices = &model.indices; vertexBuffer = &model.vertexBuffer; uvBuffer = &model.uvBuffer; normalBuffer = &model.normalBuffer; elementBuffer = &model.elementBuffer; modelTexture = &texture.texture; shaderProgram = &shader.shaderProgram; matrixID = glGetUniformLocation(*shaderProgram, "MVP"); textureID = glGetUniformLocation(*shaderProgram, "textureSampler"); } }; class Scene { public: std::vector assets; void addAsset(Asset *asset) { assets.push_back(asset); } }; GLFWwindow *window; static bool shouldExit() { return glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS && glfwWindowShouldClose(window) == 0; } glm::mat4 mvp; // TODO: REMOVE THIS PLLAAAHSE glm::mat4 viewMatrix; glm::mat4 projectionMatrix; static int initWindow() { glfwSetErrorCallback(glfwErrorCallback); if (!glfwInit()) { clog_log(CLOG_LEVEL_ERROR, "GLFW went shitty time (failed to init)"); return 1; } glfwWindowHint(GLFW_SAMPLES, 4); // 4x Antialiasing glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Loser MacOS is broken glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // New GL window = glfwCreateWindow(WIDTH, HEIGHT, "Fred", NULL, NULL); if (window == NULL) { clog_log(CLOG_LEVEL_ERROR, "Failed to open window."); glfwTerminate(); return 1; } glfwMakeContextCurrent(window); glfwSwapInterval(1); // V-Sync int version; if ((version = gladLoadGL(glfwGetProcAddress))) { clog_log(CLOG_LEVEL_DEBUG, "GL version: %d.%d", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)); } else { clog_log(CLOG_LEVEL_ERROR, "Failed to init GL!"); return 1; } IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO &io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; ImGui::StyleColorsDark(); ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init("#version 330"); // I love glm // Projection matrix, 45deg FOV, 4:3 Aspect Ratio, Planes: 0.1units -> // 100units projectionMatrix = glm::perspective( glm::radians(45.0f), (float)WIDTH / (float)HEIGHT, 0.1f, 100.0f); // Camera matrix viewMatrix = glm::lookAt(glm::vec3(4, 3, 3), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0)); glm::mat4 model = glm::mat4(1.0f); // Model view projection, the combination of the three vectors mvp = projectionMatrix * viewMatrix * model; glEnable(GL_DEPTH_TEST); // Turn on the Z-buffer glDepthFunc(GL_LESS); // Accept only the closest fragments glEnable(GL_CULL_FACE); // Backface culling glClearColor(0.1f, 0.1f, 0.1f, 1.0f); GLuint vertexArrayID; glGenVertexArrays(1, &vertexArrayID); glBindVertexArray(vertexArrayID); // Define camera stuff //glm::vec3 position = {4, 3, 3}; //float horizontalAngle = 3.14f; //float verticalAngle = -0.1f; //float initialFOV = 60.0f; //float speed = 3.0f; //float mouseSpeed = 0.005f; //double xpos, ypos; //glDeleteVertexArrays(1, &vertexArrayID); //ImGui_ImplOpenGL3_Shutdown(); //ImGui_ImplGlfw_Shutdown(); //ImGui::DestroyContext(); //glfwDestroyWindow(window); //glfwTerminate(); return 0; } //void imguiMat4Table(glm::mat4 matrix, const char *name) { // if (ImGui::BeginTable(name, 4)) { // for (int i = 0; i < 4; i++) { // for (int j = 0; j < 4; j++) { // ImGui::TableNextColumn(); // ImGui::Text("%f", matrix[i][j]); // } // } // ImGui::EndTable(); // } //} void render(Scene scene) { // Clear this mf glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); ImGuizmo::BeginFrame(); for (int i = 0; i < scene.assets.size(); i++) { Asset *currentAsset = scene.assets[i]; glUseProgram(*currentAsset->shaderProgram); // Send mega sigma MVP to the vertex shader (transformations for the win) glm::mat4 rotationMatrix = mat4_cast(currentAsset->rotation); glm::mat4 translationMatrix = glm::translate(glm::mat4(1), currentAsset->position); glm::mat4 scalingMatrix = glm::scale(glm::mat4(1), currentAsset->scaling); glm::mat4 modelMatrix = translationMatrix * rotationMatrix * scalingMatrix; mvp = projectionMatrix * viewMatrix * modelMatrix; char assetName[] = "Asset: 00"; sprintf(assetName, "Asset: %d", i); ImGui::Begin(assetName); static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE); static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD); if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE)) { mCurrentGizmoOperation = ImGuizmo::TRANSLATE; } ImGui::SameLine(); if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE)) { mCurrentGizmoOperation = ImGuizmo::ROTATE; } ImGui::SameLine(); if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::TRANSLATE)) { mCurrentGizmoOperation = ImGuizmo::SCALE; } glm::vec3 rotationEuler = eulerAngles(currentAsset->rotation); ImGui::DragFloat3("Translate", (float*)¤tAsset->position); ImGui::DragFloat3("Rotate", (float*)&rotationEuler); ImGui::DragFloat3("Scale", (float*)¤tAsset->scaling); ImGuiIO& io = ImGui::GetIO(); ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y); ImGuizmo::Manipulate((const float*)&viewMatrix, (const float*)&projectionMatrix, mCurrentGizmoOperation, mCurrentGizmoMode, (float*)&modelMatrix, NULL, NULL); ImGuizmo::DecomposeMatrixToComponents((const float*)&modelMatrix, (float *)¤tAsset->position, (float *)&rotationEuler, (float *)¤tAsset->scaling); currentAsset->rotation = glm::quat(rotationEuler); ImGui::End(); glUniformMatrix4fv(currentAsset->matrixID, 1, GL_FALSE, &mvp[0][0]); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, *currentAsset->modelTexture); // Set sampler texture glUniform1i(currentAsset->textureID, 0); // DRAWING HAPPENS HERE // Vertex Data glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, *currentAsset->vertexBuffer); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void *)0); // UV Data glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, *currentAsset->uvBuffer); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void *)0); // Normal Data glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, *currentAsset->normalBuffer); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (void *)0); // Index buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *currentAsset->elementBuffer); glDrawElements(GL_TRIANGLES, currentAsset->indices->size(), GL_UNSIGNED_SHORT, (void *)0); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glDisableVertexAttribArray(2); } ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); // Swap buffers glfwSwapBuffers(window); glfwPollEvents(); } } // namespace fred int main() { fred::initWindow(); fred::Model coneModel("../models/model.obj"); fred::Texture buffBlackGuy("../textures/results/texture_BMP_DXT5_3.DDS"); fred::Shader basicShader("../shaders/shader.vert", "../shaders/shader.frag"); fred::Asset cone(coneModel, buffBlackGuy, basicShader); fred::Asset coneTwo(coneModel, buffBlackGuy, basicShader); fred::Scene scene = fred::Scene(); scene.addAsset(&cone); scene.addAsset(&coneTwo); while (fred::shouldExit()) { //while (1) { fred::render(scene); cone.position.x += 0.01; glm::vec3 eulerAngles = glm::eulerAngles(coneTwo.rotation); eulerAngles.x += glm::radians(1.0f); coneTwo.rotation = glm::quat(eulerAngles); } return 0; }