SWBF2-Classic-Msh-Viewer/MshViewer/Source/OpenGlController.cpp

500 lines
12 KiB
C++

#include "OpenGLController.h"
#include "callback.h"
#include "shader.hpp"
#include "Texture.h"
#include <Windows.h>
#include <string>
#include <glm\gtc\matrix_transform.hpp>
#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<std::string> 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<Vertex> 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<std::uint8_t>({ 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<std::uint8_t>({ 0, 0, 255, 255 });
vTextures.push_back(tempData);
tempTexList.clear();
}