#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 vertices; std::vector indices; std::vector textures; unsigned int VAO; Mesh(std::vector _vertices, std::vector _indices, std::vector _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(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 textures_loaded; std::vector 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 vertices; std::vector indices; std::vector 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 diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse"); textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end()); std::vector specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular"); textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); std::vector normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal"); textures.insert(textures.end(), normalMaps.begin(), normalMaps.end()); std::vector heightMaps = loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_height"); textures.insert(textures.end(), heightMaps.begin(), heightMaps.end()); return Mesh(vertices, indices, textures); } std::vector loadMaterialTextures(aiMaterial* mat, aiTextureType type, std::string typeName) { std::vector 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(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(xpos - lastMouseX); float yoffset = static_cast(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()); }