#include "OpenGLController.h" #include "callback.h" #include "shader.hpp" #include "Texture.h" #include #include #include #define VERTEX_SHADER "Shader/TextureShader.vert" #define FRAGMENT_SHADER "Shader/TextureShader.frag" ///////////////////////////////////////////////////////////////////////// // public constructor/destructor OpenGLController* OpenGLController::getInstance(int oglMajor, int oglMinor) { static OpenGLController *instace = new OpenGLController(oglMajor, oglMinor); return instace; } OpenGLController::~OpenGLController() { glDeleteBuffers(1, &gluiVertexBufferID); glDeleteVertexArrays(1, &gluiVertexArrayID); glDeleteProgram(gluiShaderPrgmID); glDeleteTextures(1, &gluiSamplerID); glfwTerminate(); deleteVectors(); } ///////////////////////////////////////////////////////////////////////// // private constructor OpenGLController::OpenGLController(int oglMajor, int oglMinor) { // adjust ogl version optional iOglMajorVersion = oglMajor; iOglMinorVersion = oglMinor; // run OGL processInit(); } ///////////////////////////////////////////////////////////////////////// // private functions void OpenGLController::processInit() { startGLFW(); createWindow(); startGLEW(); setCallbackFunctions(); // set background color glClearColor(0.5000f, 0.8000f, 1.0000f, 0.0000f); // enable z-order glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); // draw vertics only from one side glEnable(GL_CULL_FACE); // generate stuff glGenVertexArrays(1, &gluiVertexArrayID); glBindVertexArray(gluiVertexArrayID); glGenBuffers(1, &gluiVertexBufferID); // open attribute position glBindBuffer(GL_ARRAY_BUFFER, gluiVertexBufferID); glVertexAttribPointer(VERTEX_INDEX_XYZ, VERTEX_COMPONENTS_XYZ, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)VERTEX_OFFSET_XYZ); glVertexAttribPointer(VERTEX_INDEX_UV, VERTEX_COMPONENTS_UV, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)VERTEX_OFFSET_UV); glBindBuffer(GL_ARRAY_BUFFER, 0); // enable position, UV and mvp glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); // get the painter ready try { gluiShaderPrgmID = LoadShaders(VERTEX_SHADER, FRAGMENT_SHADER); } catch (std::invalid_argument e) { MessageBox(NULL, e.what(), "MeshViewer 2.0 Error", MB_OK | MB_ICONERROR); exit(1); } // get some shader IDs gluiMatrixID = glGetUniformLocation(gluiShaderPrgmID, "MVP"); gluiSamplerID = glGetUniformLocation(gluiShaderPrgmID, "textureSampler"); // generate texture glGenTextures(1, &gluiTextureID); glBindTexture(GL_TEXTURE_2D, gluiTextureID); // set some texture parameters 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_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } void OpenGLController::deleteVectors() { if (vModels != NULL) { while (!vModels->empty()) { Modl* modelVectorElement = vModels->back(); vModels->pop_back(); while (!modelVectorElement->segmLst.empty()) { Segment* segmentVectorElement = modelVectorElement->segmLst.back(); modelVectorElement->segmLst.pop_back(); delete[] segmentVectorElement->uv; delete[] segmentVectorElement->vertex; while (!segmentVectorElement->meshIndices.empty()) { segmentVectorElement->meshIndices.back().clear(); segmentVectorElement->meshIndices.pop_back(); } delete segmentVectorElement; } delete modelVectorElement; } delete vModels; } while (!vTextures.empty()) { textureData* cursor = vTextures.back(); vTextures.pop_back(); cursor->data->clear(); delete cursor->data; delete cursor; } } void OpenGLController::startGLFW() { if (!glfwInit()) { MessageBox(NULL, "Failed to initialize GLFW", "MeshViewer 2.0 Error", MB_OK | MB_ICONERROR); exit(0); } glfwWindowHint(GLFW_SAMPLES, iAntiAliasingLevel); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, iOglMajorVersion); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, iOglMinorVersion); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); } void OpenGLController::createWindow() { pWindow = glfwCreateWindow(iWidth, iHeight, sWindowName.c_str(), NULL, NULL); if (pWindow == NULL) { std::string message = "Your GPU does not support OpenGL "; message += std::to_string(iOglMajorVersion); message += "."; message += std::to_string(iOglMinorVersion); message += "\nTry to use an other version"; MessageBox(NULL, message.c_str(), "MeshViewer 2.0 Error", MB_OK | MB_ICONERROR); glfwTerminate(); exit(0); } glfwSetWindowUserPointer(pWindow, this); glfwMakeContextCurrent(pWindow); } void OpenGLController::startGLEW() { glewExperimental = true; if (glewInit() != GLEW_OK) { MessageBox(NULL, "Failed to initialize GLEW", "MeshViewer 2.0 Error", MB_OK | MB_ICONERROR); glfwTerminate(); exit(0); } } void OpenGLController::setCallbackFunctions() { glfwSetMouseButtonCallback(pWindow, mouseButton); glfwSetCursorPosCallback(pWindow, mouseMove); glfwSetWindowSizeCallback(pWindow, windowResize); glfwSetScrollCallback(pWindow, mouseWheel); glfwSetKeyCallback(pWindow, keyPress); glfwSetDropCallback(pWindow, dragNdrop); } glm::mat4 OpenGLController::getModelMatrix(unsigned int index) { glm::mat4 tempParentMatrix = glm::mat4(1.0f); for (unsigned int loop = 0; loop < vModels->size(); loop++) { if (!strcmp(vModels->at(index)->parent.c_str(), vModels->at(loop)->name.c_str())) { tempParentMatrix = getModelMatrix(loop); break; } } return tempParentMatrix * vModels->at(index)->m4x4Translation; } glm::mat4 OpenGLController::getMVPMatrix(unsigned int index) { // Projection glm::mat4 m4x4Projection = glm::perspective(fFOV, float(iWidth) / float(iHeight), fMinView, fMaxView); // View glm::mat4 m4x4View = glm::lookAt( glm::vec3(dTranslationX, dTranslationY, dTranslationZ), glm::vec3(dTranslationX, dTranslationY, dTranslationZ - 1), glm::vec3(0, 1, 0) ); // User controlled rotation glm::mat4 m4x4ModelRot = glm::mat4(1.0f); m4x4ModelRot = glm::rotate(m4x4ModelRot, fRotationX, glm::vec3(1, 0, 0)); m4x4ModelRot = glm::rotate(m4x4ModelRot, fRotationY, glm::vec3(0, 1, 0)); m4x4ModelRot = glm::rotate(m4x4ModelRot, fRotationZ, glm::vec3(0, 0, 1)); // move to center glm::mat4 m4x4ModelCenter = glm::translate( glm::mat4(1.0f), glm::vec3(-sceneBoundingBox.center[0], -sceneBoundingBox.center[1], -sceneBoundingBox.center[2]) ); //scale to 1 float maxExtent = max(max(sceneBoundingBox.extents[0], sceneBoundingBox.extents[1]), sceneBoundingBox.extents[2]); glm::mat4 m4x4Normalize = glm::mat4(1.0f); m4x4Normalize = glm::scale(m4x4Normalize, glm::vec3(1/maxExtent, 1 / maxExtent, 1 / maxExtent)); // Return MVP return m4x4Projection * m4x4View * m4x4ModelRot * m4x4Normalize * m4x4ModelCenter * getModelMatrix(index); } ///////////////////////////////////////////////////////////////////////// // public getter GLFWwindow * OpenGLController::getWindow() const { return pWindow; } ///////////////////////////////////////////////////////////////////////// // public functions void OpenGLController::resize(int width, int height) { iWidth = width; iHeight = height; } void OpenGLController::addRotX(float value) { fRotationX += value; } void OpenGLController::addRotY(float value) { fRotationY += value; } void OpenGLController::addTransX(double value) { dTranslationX += value; } void OpenGLController::addTransY(double value) { dTranslationY += value; } void OpenGLController::addTransZ(double value) { dTranslationZ += value; } void OpenGLController::resetView() { dTranslationX = 0; dTranslationY = 0; dTranslationZ = 5; fRotationX = 0; fRotationY = 0; fRotationZ = 0; } void OpenGLController::updateScene() { // get new matrices glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // use shader prgm glUseProgram(gluiShaderPrgmID); // bind texture in texture unit 0 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, gluiTextureID); // tell sampler to use texture unit 0 glUniform1i(gluiSamplerID, 0); if (vModels != NULL) { int instanceOffset(0); for (unsigned int modelIndex = 0; modelIndex < vModels->size(); modelIndex++) { // skip null, bones, shadowMesh, hidden things (don't increase textrue index!!) if (vModels->at(modelIndex)->type == null || vModels->at(modelIndex)->type == bone || vModels->at(modelIndex)->type == shadowMesh || vModels->at(modelIndex)->renderFlags == 1) continue; for (auto& segIt : vModels->at(modelIndex)->segmLst) { // give texture to the shader std::uint32_t tempTexIndex = segIt->textureIndex >= vTextures.size() ? vTextures.size() - 1 : segIt->textureIndex; glTexImage2D( GL_TEXTURE_2D, 0, vTextures[tempTexIndex]->alpha ? GL_RGBA : GL_RGB, vTextures[tempTexIndex]->width, vTextures[tempTexIndex]->height, 0, vTextures[tempTexIndex]->alpha ? GL_BGRA : GL_BGR, GL_UNSIGNED_BYTE, vTextures[tempTexIndex]->data->data() ); glGenerateMipmap(GL_TEXTURE_2D); // give the MVP to the shader glUniformMatrix4fv(gluiMatrixID, 1, GL_FALSE, &getMVPMatrix(modelIndex)[0][0]); // calculate the number of vertex unsigned int vertexCount(0); for (auto& it : segIt->meshIndices) vertexCount += (it.size() - 2) * 3; glDrawArrays(GL_TRIANGLES, instanceOffset, vertexCount); // increase the offset instanceOffset += vertexCount; } } } glfwSwapBuffers(pWindow); glfwPollEvents(); } void OpenGLController::loadMsh(const char * path) { // clean up old stuff first deleteVectors(); std::vector tempTexList; // get all models try { Object obj(path); vModels = obj.getModels(); tempTexList = obj.getTextureList(); sceneBoundingBox = obj.getBoundgBox(); } catch (std::invalid_argument e) { MessageBox(NULL, e.what(), "MeshViewer 2.0 Error", MB_OK | MB_ICONERROR); return; } // collect vertex data of all models std::vector tempBufferData; for (auto& modIt : *vModels) // for every model chunk { for (auto& segIt : modIt->segmLst) // for every cluster { for (auto& mshIt : segIt->meshIndices) // for every polygon { if (mshIt.size() >= 3) // multipoly { // for every triangle of the multi polygon for (unsigned int tri = 0; tri < mshIt.size() - 2; tri++) { // for every edge of the triangle for (int triEdge = 0; triEdge < 3; triEdge++) { Vertex tempVertex; // every edge has 3 coordinates for (int j = 0; j < 3; j++) tempVertex.position[j] = (GLfloat)segIt->vertex[mshIt[tri + triEdge - ((tri % 2) * (triEdge - 1) * 2)] * 3 + j]; // and 2 UV if (segIt->uv == NULL) { tempVertex.uv[0] = 1.0; tempVertex.uv[1] = 1.0; } else { for (int j = 0; j < 2; j++) tempVertex.uv[j] = (GLfloat)segIt->uv[mshIt[tri + triEdge - ((tri % 2) * (triEdge - 1) * 2)] * 2 + j]; } tempBufferData.push_back(tempVertex); } } } else // this shouldn't happen (polygon with less then 3 vertex) { deleteVectors(); MessageBox(NULL, "You have polygons with less then 3 vertices!", "MeshViewer 2.0 Error", MB_OK | MB_ICONERROR); return; } } } } // fill the buffer glBindBuffer(GL_ARRAY_BUFFER, gluiVertexBufferID); glBufferData( GL_ARRAY_BUFFER, sizeof(Vertex) * tempBufferData.size(), tempBufferData.data(), GL_STATIC_DRAW ); glBindBuffer(GL_ARRAY_BUFFER, 0); tempBufferData.clear(); // get textures path std::string tempPath = path; while (tempPath.back() != '/' && tempPath.back() != '\\') tempPath.pop_back(); // load all textures; for (auto& texIt : tempTexList) { textureData* tempData = new textureData; try { TextureTGA tempTex(std::string(tempPath + texIt).c_str()); tempData->alpha = tempTex.hasAlpha(); tempData->width = tempTex.getWidth(); tempData->height = tempTex.getHeight(); tempData->data = tempTex.getData(); } catch (std::invalid_argument e) { tempData->alpha = true; tempData->width = 1; tempData->height = 1; tempData->data = new std::vector({ 0, 0, 255, 255 }); } vTextures.push_back(tempData); } // add a solid default color at the end (maybe there is an invalid index later) textureData* tempData = new textureData; tempData->alpha = true; tempData->width = 1; tempData->height = 1; tempData->data = new std::vector({ 0, 0, 255, 255 }); vTextures.push_back(tempData); tempTexList.clear(); }