Files
XCEngine/MVS/OpenGL/main.cpp

723 lines
20 KiB
C++

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <cmath>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <stb_image.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
enum Camera_Movement {
CAMERA_FORWARD,
CAMERA_BACKWARD,
CAMERA_RIGHT,
CAMERA_LEFT
};
class Shader;
class Model;
class Camera;
class Mesh;
class Shader
{
public:
unsigned int ID;
Shader(const char* vertexPath, const char* fragmentPath, const char* geometryPath = nullptr)
{
std::string vertexCode;
std::string fragmentCode;
std::string geometryCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
std::ifstream gShaderFile;
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
gShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try
{
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
vShaderFile.close();
fShaderFile.close();
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
if (geometryPath != nullptr)
{
gShaderFile.open(geometryPath);
std::stringstream gShaderStream;
gShaderStream << gShaderFile.rdbuf();
gShaderFile.close();
geometryCode = gShaderStream.str();
}
}
catch (std::ifstream::failure& e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ: " << e.what() << std::endl;
}
const char* vShaderCode = vertexCode.c_str();
const char* fShaderCode = fragmentCode.c_str();
unsigned int vertex = 0, fragment;
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
unsigned int geometry;
if (geometryPath != nullptr)
{
const char* gShaderCode = geometryCode.c_str();
geometry = glCreateShader(GL_GEOMETRY_SHADER);
glShaderSource(geometry, 1, &gShaderCode, NULL);
glCompileShader(geometry);
checkCompileErrors(geometry, "GEOMETRY");
}
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
if (geometryPath != nullptr)
glAttachShader(ID, geometry);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
glDeleteShader(vertex);
glDeleteShader(fragment);
if (geometryPath != nullptr)
glDeleteShader(geometry);
}
void use()
{
glUseProgram(ID);
}
void setBool(const std::string& name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
void setInt(const std::string& name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
void setFloat(const std::string& name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
void setVec2(const std::string& name, const glm::vec2& value) const
{
glUniform2fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
}
void setVec2(const std::string& name, float x, float y) const
{
glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y);
}
void setVec3(const std::string& name, const glm::vec3& value) const
{
glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
}
void setVec3(const std::string& name, float x, float y, float z) const
{
glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z);
}
void setVec4(const std::string& name, const glm::vec4& value) const
{
glUniform4fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
}
void setVec4(const std::string& name, float x, float y, float z, float w)
{
glUniform4f(glGetUniformLocation(ID, name.c_str()), x, y, z, w);
}
void setMat2(const std::string& name, const glm::mat2& mat) const
{
glUniformMatrix2fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
void setMat3(const std::string& name, const glm::mat3& mat) const
{
glUniformMatrix3fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
void setMat4(const std::string& name, const glm::mat4& mat) const
{
glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
private:
void checkCompileErrors(GLuint shader, std::string type)
{
GLint success;
GLchar infoLog[1024];
if (type != "PROGRAM")
{
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
else
{
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
}
};
#define MAX_BONE_INFLUENCE 4
struct Vertex
{
glm::vec3 Position;
glm::vec3 Normal;
glm::vec2 TexCoords;
glm::vec3 Tangent;
glm::vec3 Bitangent;
int m_BoneIDs[MAX_BONE_INFLUENCE];
float m_Weights[MAX_BONE_INFLUENCE];
};
struct Texture
{
unsigned int id;
std::string type;
std::string path;
};
unsigned int TextureFromFile(const char* path, const std::string& directory, bool gamma = false);
class Mesh
{
public:
std::vector<Vertex> vertices;
std::vector<unsigned int> indices;
std::vector<Texture> textures;
unsigned int VAO;
Mesh(std::vector<Vertex> _vertices, std::vector<unsigned int> _indices, std::vector<Texture> _textures)
{
vertices = _vertices;
indices = _indices;
textures = _textures;
setupMesh();
}
void Draw(Shader* shader)
{
unsigned int diffuseNr = 1;
unsigned int specularNr = 1;
unsigned int normalNr = 1;
unsigned int heightNr = 1;
for (unsigned int i = 0; i < textures.size(); i++)
{
glActiveTexture(GL_TEXTURE0 + i);
std::string number;
std::string name = textures[i].type;
if (name == "texture_diffuse")
number = std::to_string(diffuseNr++);
else if (name == "texture_specular")
number = std::to_string(specularNr++);
else if (name == "texture_normal")
number = std::to_string(normalNr++);
else if (name == "texture_height")
number = std::to_string(heightNr++);
glUniform1i(glGetUniformLocation(shader->ID, ("material." + name + number).c_str()), i);
glBindTexture(GL_TEXTURE_2D, textures[i].id);
}
glBindVertexArray(VAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glDrawElements(GL_TRIANGLES, static_cast<unsigned int>(indices.size()), GL_UNSIGNED_INT, 0);
glActiveTexture(GL_TEXTURE0);
}
private:
unsigned int VBO, EBO;
void setupMesh()
{
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Tangent));
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Bitangent));
glEnableVertexAttribArray(5);
glVertexAttribIPointer(5, 4, GL_INT, sizeof(Vertex), (void*)offsetof(Vertex, m_BoneIDs));
glEnableVertexAttribArray(6);
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, m_Weights));
glBindVertexArray(0);
}
};
unsigned int TextureFromFile(const char* path, const std::string& directory, bool gamma)
{
std::string filename = std::string(path);
filename = directory + '/' + filename;
unsigned int textureID;
glGenTextures(1, &textureID);
int width, height, nrComponents;
stbi_set_flip_vertically_on_load(true);
unsigned char* data = stbi_load(filename.c_str(), &width, &height, &nrComponents, 0);
if (data)
{
GLenum format;
if (nrComponents == 1)
format = GL_RED;
else if (nrComponents == 3)
format = GL_RGB;
else if (nrComponents == 4)
format = GL_RGBA;
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
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, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
}
else
{
std::cout << "Texture failed to load at path: " << path << std::endl;
stbi_image_free(data);
}
return textureID;
}
class Model
{
public:
std::vector<Texture> textures_loaded;
std::vector<Mesh> meshes;
std::string directory;
bool gammaCorrection;
Model(std::string const& path, bool gamma = false) : gammaCorrection(gamma)
{
loadModel(path);
}
void Draw(Shader* shader)
{
for (unsigned int i = 0; i < meshes.size(); i++)
{
meshes[i].Draw(shader);
}
}
private:
void loadModel(std::string const& path)
{
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs | aiProcess_CalcTangentSpace);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
{
std::cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << std::endl;
return;
}
directory = path.substr(0, path.find_last_of('/'));
processNode(scene->mRootNode, scene);
}
void processNode(aiNode* node, const aiScene* scene)
{
for (unsigned int i = 0; i < node->mNumMeshes; i++)
{
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(processMesh(mesh, scene));
}
for (unsigned int i = 0; i < node->mNumChildren; i++)
{
processNode(node->mChildren[i], scene);
}
}
Mesh processMesh(aiMesh* mesh, const aiScene* scene)
{
std::vector<Vertex> vertices;
std::vector<unsigned int> indices;
std::vector<Texture> textures;
for (unsigned int i = 0; i < mesh->mNumVertices; i++)
{
Vertex vertex;
glm::vec3 vector;
vector.x = mesh->mVertices[i].x;
vector.y = mesh->mVertices[i].y;
vector.z = mesh->mVertices[i].z;
vertex.Position = vector;
if (mesh->HasNormals())
{
vector.x = mesh->mNormals[i].x;
vector.y = mesh->mNormals[i].y;
vector.z = mesh->mNormals[i].z;
vertex.Normal = vector;
}
if (mesh->mTextureCoords[0])
{
glm::vec2 vec;
vec.x = mesh->mTextureCoords[0][i].x;
vec.y = mesh->mTextureCoords[0][i].y;
vertex.TexCoords = vec;
vector.x = mesh->mTangents[i].x;
vector.y = mesh->mTangents[i].y;
vector.z = mesh->mTangents[i].z;
vertex.Tangent = vector;
vector.x = mesh->mBitangents[i].x;
vector.y = mesh->mBitangents[i].y;
vector.z = mesh->mBitangents[i].z;
vertex.Bitangent = vector;
}
else
vertex.TexCoords = glm::vec2(0.0f, 0.0f);
vertices.push_back(vertex);
}
for (unsigned int i = 0; i < mesh->mNumFaces; i++)
{
aiFace face = mesh->mFaces[i];
for (unsigned int j = 0; j < face.mNumIndices; j++)
indices.push_back(face.mIndices[j]);
}
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
std::vector<Texture> diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
std::vector<Texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
std::vector<Texture> normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal");
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
std::vector<Texture> heightMaps = loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_height");
textures.insert(textures.end(), heightMaps.begin(), heightMaps.end());
return Mesh(vertices, indices, textures);
}
std::vector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type, std::string typeName)
{
std::vector<Texture> textures;
for (unsigned int i = 0; i < mat->GetTextureCount(type); i++)
{
aiString str;
mat->GetTexture(type, i, &str);
bool skip = false;
for (unsigned int j = 0; j < textures_loaded.size(); j++)
{
if (std::strcmp(textures_loaded[j].path.data(), str.C_Str()) == 0)
{
textures.push_back(textures_loaded[j]);
skip = true;
break;
}
}
if (!skip)
{
Texture texture;
texture.id = TextureFromFile(str.C_Str(), this->directory);
texture.type = typeName;
texture.path = str.C_Str();
textures.push_back(texture);
textures_loaded.push_back(texture);
}
}
return textures;
}
};
class Camera
{
public:
float moveSpeed = 1.5f;
float mouseSensitivity = 0.1f;
Camera(glm::vec3 _position, float _yaw = 0.0f, float _pitch = 0.0f, float _fov = 45.0f)
{
position = _position;
yaw = _yaw;
pitch = _pitch;
zoom = _fov;
glm::mat4 translate = glm::translate(identity, position);
glm::mat4 rotate = glm::rotate(identity, glm::radians(yaw), glm::vec3(0.0f, 1.0f, 0.0f));
rotate = glm::rotate(rotate, glm::radians(pitch), glm::vec3(1.0f, 0.0f, 0.0f));
trans = translate * rotate;
}
void Move(Camera_Movement move, float delta)
{
glm::vec3 dPosition = glm::vec3(0.0f);
switch (move)
{
case CAMERA_FORWARD:
dPosition = front * moveSpeed * delta;
break;
case CAMERA_BACKWARD:
dPosition = back * moveSpeed * delta;
break;
case CAMERA_LEFT:
dPosition = left * moveSpeed * delta;
break;
case CAMERA_RIGHT:
dPosition = right * moveSpeed * delta;
break;
}
glm::mat4 translate = glm::translate(identity, dPosition);
trans = trans * translate;
}
void Rotate(float xoffset, float yoffset)
{
float dYaw = 0, dPitch = 0;
dYaw = -xoffset * mouseSensitivity;
if (pitch < 89.0f && yoffset < 0 || pitch > -89.0 && yoffset > 0)
{
dPitch = -yoffset * mouseSensitivity;
}
yaw += dYaw;
pitch += dPitch;
glm::mat4 rotate = glm::rotate(identity, glm::radians(dYaw), glm::vec3(0.0f, 1.0f, 0.0f));
rotate = glm::rotate(rotate, glm::radians(dPitch), glm::vec3(1.0f, 0.0f, 0.0f));
trans = trans * rotate;
}
void Scale(float offset)
{
if (zoom > 75 || zoom < 1) { return; }
zoom -= (float)offset;
}
glm::mat4 GetTrans()
{
return trans;
}
glm::vec3 GetPos()
{
glm::vec3 pos(trans[3][0], trans[3][1], trans[3][2]);
return pos;
}
private:
glm::vec3 position;
glm::vec3 rotation;
glm::mat4 trans;
float pitch;
float yaw;
float zoom;
const glm::vec3 front = glm::vec3(0, 0, -1);
const glm::vec3 back = glm::vec3(0, 0, 1);
const glm::vec3 left = glm::vec3(-1, 0, 0);
const glm::vec3 right = glm::vec3(1, 0, 0);
const glm::mat4 identity = glm::mat4(1.0f);
};
glm::vec3 lightColor = glm::vec3(1.0, 1.0, 1.0);
glm::vec3 lightPosition = glm::vec3(4.0f, 4.0f, -4.0f);
const glm::mat4 identityGlobal = glm::mat4(1.0f);
glm::mat4 origine = identityGlobal;
int lastMouseX;
int lastMouseY;
float delta = 0.0f;
float lastFrame = 0.0f;
float currentFrame = 0.0f;
Model* model;
Shader* shader;
Camera* camera;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void processInput(GLFWwindow* window);
void Initialize();
void Render();
void Transform();
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "XCRender", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, mouse_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
Initialize();
while (!glfwWindowShouldClose(window))
{
currentFrame = static_cast<float>(glfwGetTime());
delta = currentFrame - lastFrame;
lastFrame = currentFrame;
processInput(window);
Render();
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
if (glfwGetMouseButton(window, 1) == GLFW_FALSE)
return;
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
camera->Move(CAMERA_FORWARD, delta);
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
camera->Move(CAMERA_BACKWARD, delta);
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
camera->Move(CAMERA_LEFT, delta);
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
camera->Move(CAMERA_RIGHT, delta);
}
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (glfwGetMouseButton(window, 1) == GLFW_RELEASE)
{
lastMouseX = (int)xpos;
lastMouseY = (int)ypos;
return;
}
float xoffset = static_cast<float>(xpos - lastMouseX);
float yoffset = static_cast<float>(ypos - lastMouseY);
lastMouseX = (int)xpos;
lastMouseY = (int)ypos;
camera->Rotate(xoffset, yoffset);
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
void Initialize()
{
camera = new Camera(glm::vec3(-1, 0, 3), 0, 0);
model = new Model("res/models/backpack/backpack.obj");
shader = new Shader("Shaders/vertexshader.glsl", "Shaders/fragmentshader.glsl");
glEnable(GL_DEPTH_TEST);
}
void Render()
{
glClearColor(0, 0, 0, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader->use();
Transform();
shader->setFloat("material.shininess", 32.0f);
shader->setVec3("dirLight.direction", 0.0f, -1.0f, 0.0f);
shader->setVec3("dirLight.ambient", 0.1f, 0.1f, 0.1f);
shader->setVec3("dirLight.diffuse", 0.4f, 0.4f, 0.4f);
shader->setVec3("dirLight.specular", 0.5f, 0.5f, 0.5f);
shader->setInt("PointLightNum", 3);
shader->setVec3("pointLights[0].position", glm::vec3(0.0f, -1.0f, 0.0f));
shader->setVec3("pointLights[0].ambient", 0.05f, 0.05f, 0.05f);
shader->setVec3("pointLights[0].diffuse", 0.8f, 0.8f, 0.8f);
shader->setVec3("pointLights[0].specular", 1.0f, 1.0f, 1.0f);
shader->setFloat("pointLights[0].constant", 1.0f);
shader->setFloat("pointLights[0].linear", 0.09f);
shader->setFloat("pointLights[0].quadratic", 0.032f);
model->Draw(shader);
}
void Transform()
{
glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
origine = glm::rotate(origine, glm::radians(-0.01f), glm::vec3(1.0f, 1.0f, 1.0f));
glm::mat4 modelMat = origine;
glm::mat4 cameraTrans = camera->GetTrans();
glm::mat4 view = glm::inverse(cameraTrans);
shader->setMat4("view", view);
shader->setMat4("model", modelMat);
shader->setMat4("projection", projection);
shader->setVec3("viewPos", camera->GetPos());
}