-
-
Save anonymous/b10a61e337457f9ec0b661509d2df95d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#version 330 core | |
out vec4 color; | |
uniform vec3 objectColor; | |
uniform vec3 lightColor; | |
uniform vec3 lightPosition; | |
uniform vec3 viewPosition; | |
in vec3 Normal; | |
in vec3 FragPos; | |
in vec2 TexCoords; | |
uniform sampler2D texture1; | |
void main() | |
{ | |
float ambientStrength = 0.1f; | |
vec3 ambient = ambientStrength * lightColor; | |
vec3 norm = normalize(Normal); | |
vec3 lightDirection = normalize(lightPosition - FragPos); | |
float diff = max(dot(norm, lightDirection), 0.0f); | |
vec3 diffuse = diff * lightColor; | |
float specularIntensity = 0.5f; | |
vec3 viewDirection = normalize(viewPosition - FragPos); | |
vec3 reflectDirection = reflect(-lightDirection, norm); | |
float spec = pow(max(dot(viewDirection, reflectDirection), 0.0), 64); | |
vec3 specular = specularIntensity * spec * lightColor; | |
vec3 result = (ambient + diffuse + specular); | |
color = texture(texture1, TexCoords) * vec4(result, 1.0f); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#version 330 core | |
layout (location = 0) in vec3 position; | |
layout (location = 1) in vec3 normal; | |
layout (location = 2) in vec2 texCoordinates; | |
out vec3 Normal; | |
out vec3 FragPos; | |
out vec2 TexCoords; | |
uniform mat4 model; | |
uniform mat4 view; | |
uniform mat4 projection; | |
void main() | |
{ | |
gl_Position = projection * view * model * vec4(position, 1.0f); | |
FragPos = vec3(model * vec4(position, 1.0f)); | |
Normal = mat3(transpose(inverse(model))) * normal; | |
TexCoords = texCoordinates; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <glad/glad.h> | |
#include <GLFW/glfw3.h> | |
#include <glm/glm.hpp> | |
#include <glm/gtc/matrix_transform.hpp> | |
#include <glm/gtc/type_ptr.hpp> | |
#include <stb_image.h> | |
#include <shader.h> | |
#include <camera.h> | |
#include <model.h> | |
void framebuffer_size_callback(GLFWwindow* window, int width, int height); | |
void mouse_callback(GLFWwindow* window, double xpos, double ypos); | |
void processInput(GLFWwindow *window); | |
unsigned int loadTexture(const char *path); | |
const GLuint HEIGHT = 600, WIDTH = 800; | |
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); | |
float lastX = WIDTH / 2.0; | |
float lastY = HEIGHT / 2.0; | |
bool firstMouse = true; | |
float deltaTime = 0.0f; | |
float lastFrame = 0.0f; | |
glm::vec3 lightPosition(1.8f, 1.2f, 2.5f); | |
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(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr); | |
glfwMakeContextCurrent(window); | |
if (window == nullptr) | |
{ | |
std::cout << "Failed to create GLFW window" << std::endl; | |
glfwTerminate(); | |
return -1; | |
} | |
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); | |
glfwSetCursorPosCallback(window, mouse_callback); | |
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); | |
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) | |
{ | |
std::cout << "Failed to initialize GLAD" << std::endl; | |
return -1; | |
} | |
glEnable(GL_DEPTH_TEST); | |
Shader basicShader("basicShader.vert", "basicShader.frag"); | |
Shader lampShader("lampShader.vert", "lampShader.frag"); | |
Shader modelShader("modelShader.vert", "modelShader.frag"); | |
Model rockModel("resources/objects/rock/rock.obj"); | |
std::vector<GLfloat> vertices = { | |
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, | |
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, | |
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, | |
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, | |
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, | |
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, | |
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, | |
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, | |
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, | |
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, | |
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, | |
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, | |
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, | |
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, | |
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, | |
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, | |
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, | |
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, | |
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, | |
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, | |
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, | |
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, | |
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, | |
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, | |
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, | |
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, | |
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, | |
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, | |
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, | |
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, | |
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, | |
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, | |
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, | |
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, | |
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, | |
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, | |
}; | |
unsigned int VBO, cubeVAO; | |
glGenVertexArrays(1, &cubeVAO); | |
glBindVertexArray(cubeVAO); | |
glGenBuffers(1, &VBO); | |
glBindBuffer(GL_ARRAY_BUFFER, VBO); | |
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GLfloat), vertices.data(), GL_STATIC_DRAW); | |
glEnableVertexAttribArray(0); | |
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), static_cast<void*>(nullptr)); | |
glEnableVertexAttribArray(1); | |
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), reinterpret_cast<void*>(3 * sizeof(GLfloat))); | |
glEnableVertexAttribArray(2); | |
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), reinterpret_cast<void*>(6 * sizeof(GLfloat))); | |
glEnableVertexAttribArray(0); | |
unsigned int lightVAO; | |
glGenVertexArrays(1, &lightVAO); | |
glBindVertexArray(lightVAO); | |
glBindBuffer(GL_ARRAY_BUFFER, VBO); | |
glEnableVertexAttribArray(0); | |
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), static_cast<void*>(nullptr)); | |
glEnableVertexAttribArray(0); | |
std::vector<GLfloat> floorVertices = { | |
5.0f, -0.5f, 5.0f, 0.0f, 1.0f, 0.0f, 2.0f, 0.0f, | |
-5.0f, -0.5f, 5.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, | |
-5.0f, -0.5f, -5.0f, 0.0f, 1.0f, 0.0f, 0.0f, 2.0f, | |
5.0f, -0.5f, 5.0f, 0.0f, 1.0f, 0.0f, 2.0f, 0.0f, | |
-5.0f, -0.5f, -5.0f, 0.0f, 1.0f, 0.0f, 0.0f, 2.0f, | |
5.0f, -0.5f, -5.0f, 0.0f, 1.0f, 0.0f, 2.0f, 2.0f | |
}; | |
unsigned int floorVAO, floorVBO; | |
glGenVertexArrays(1, &floorVAO); | |
glBindVertexArray(floorVAO); | |
glGenBuffers(1, &floorVBO); | |
glBindBuffer(GL_ARRAY_BUFFER, floorVBO); | |
glBufferData(GL_ARRAY_BUFFER, floorVertices.size() * sizeof(GLfloat), floorVertices.data(), GL_STATIC_DRAW); | |
glEnableVertexAttribArray(0); | |
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), static_cast<void*>(nullptr)); | |
glEnableVertexAttribArray(1); | |
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), reinterpret_cast<void*>(3 * sizeof(GLfloat))); | |
glEnableVertexAttribArray(2); | |
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), reinterpret_cast<void*>(6 * sizeof(GLfloat))); | |
glBindVertexArray(0); | |
unsigned int floorTexture = loadTexture("resources/textures/metal.png"); | |
unsigned int box1Texture = loadTexture("resources/textures/container.jpg"); | |
unsigned int box2Texture = loadTexture("resources/textures/container2.png"); | |
float hour = 0.0; | |
float inc = 1.00; | |
while (!glfwWindowShouldClose(window)) | |
{ | |
float currentFrame = glfwGetTime(); | |
deltaTime = currentFrame - lastFrame; | |
lastFrame = currentFrame; | |
hour += inc * deltaTime; | |
processInput(window); | |
glClearColor(0.2f, 0.3f, 0.3f, 1.0f); | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
basicShader.use(); | |
basicShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f); | |
basicShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f); | |
basicShader.setVec3("lightPosition", lightPosition); | |
basicShader.setVec3("viewPosition", camera.Position); | |
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), WIDTH * 1.0f / HEIGHT, 0.1f, 100.0f); | |
basicShader.setMat4("projection", projection); | |
glm::mat4 view = camera.GetViewMatrix(); | |
basicShader.setMat4("view", view); | |
glm::mat4 model; | |
basicShader.setMat4("model", model); | |
glBindVertexArray(cubeVAO); | |
glBindTexture(GL_TEXTURE_2D, box1Texture); | |
model = glm::mat4(); | |
basicShader.setMat4("model", model); | |
glDrawArrays(GL_TRIANGLES, 0, 36); | |
glBindVertexArray(cubeVAO); | |
glBindTexture(GL_TEXTURE_2D, box2Texture); | |
model = glm::mat4(); | |
model = glm::translate(model, glm::vec3(0.0f, 1.0f, 0.0f)); | |
GLfloat t = sin(glfwGetTime()); | |
model = glm::rotate(model, 360 * hour / 365.0f, glm::vec3(0.0f, 1.0f, 0.0f)); | |
basicShader.setMat4("model", model); | |
glDrawArrays(GL_TRIANGLES, 0, 36); | |
glBindVertexArray(floorVAO); | |
glBindTexture(GL_TEXTURE_2D, floorTexture); | |
model = glm::mat4(); | |
basicShader.setMat4("model", model); | |
glDrawArrays(GL_TRIANGLES, 0, 18); | |
glBindVertexArray(0); | |
model = glm::mat4(); | |
model = glm::translate(model, glm::vec3(1.5f, 0.0f, 0.0f)); | |
model = glm::scale(model, glm::vec3(0.5f, 0.5f, 0.5f)); | |
basicShader.setMat4("model", model); | |
rockModel.Draw(basicShader); | |
glBindVertexArray(0); | |
lampShader.use(); | |
lampShader.setMat4("projection", projection); | |
lampShader.setMat4("view", view); | |
model = glm::mat4(); | |
model = glm::translate(model, lightPosition); | |
model = glm::scale(model, glm::vec3(0.10f)); | |
lampShader.setMat4("model", model); | |
glBindVertexArray(lightVAO); | |
glDrawArrays(GL_TRIANGLES, 0, 36); | |
glBindVertexArray(0); | |
glfwSwapBuffers(window); | |
glfwPollEvents(); | |
} | |
glfwTerminate(); | |
return 0; | |
} | |
void processInput(GLFWwindow *window) | |
{ | |
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) | |
glfwSetWindowShouldClose(window, true); | |
float cameraSpeed = 2.5 * deltaTime; | |
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) | |
camera.ProcessKeyboard(FORWARD, deltaTime); | |
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) | |
camera.ProcessKeyboard(BACKWARD, deltaTime); | |
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) | |
camera.ProcessKeyboard(LEFT, deltaTime); | |
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) | |
camera.ProcessKeyboard(RIGHT, deltaTime); | |
if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) | |
camera.ProcessKeyboard(UPWARD, deltaTime); | |
if (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) | |
camera.ProcessKeyboard(DOWNWARD, deltaTime); | |
} | |
void framebuffer_size_callback(GLFWwindow* window, int width, int height) | |
{ | |
glViewport(0, 0, width, height); | |
} | |
void mouse_callback(GLFWwindow* window, double xpos, double ypos) | |
{ | |
if (firstMouse) | |
{ | |
lastX = xpos; | |
lastY = ypos; | |
firstMouse = false; | |
} | |
float xoffset = xpos - lastX; | |
float yoffset = lastY - ypos; | |
lastX = xpos; | |
lastY = ypos; | |
camera.ProcessMouseMovement(xoffset, yoffset); | |
} | |
unsigned int loadTexture(char const * path) | |
{ | |
unsigned int textureID; | |
glGenTextures(1, &textureID); | |
int width, height, nrComponents; | |
unsigned char *data = stbi_load(path, &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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
// Std. Includes | |
#include <string> | |
#include <fstream> | |
#include <sstream> | |
#include <iostream> | |
#include <vector> | |
#include <assimp/types.h> | |
using namespace std; | |
// GL Includes | |
#include <glad/glad.h> // Contains all the necessery OpenGL includes | |
#include <glm/glm.hpp> | |
#include <glm/gtc/matrix_transform.hpp> | |
struct Vertex { | |
// Position | |
glm::vec3 Position; | |
// Normal | |
glm::vec3 Normal; | |
// TexCoords | |
glm::vec2 TexCoords; | |
// Tangent | |
glm::vec3 Tangent; | |
// Bitangent | |
glm::vec3 Bitangent; | |
}; | |
struct Texture { | |
GLuint id; | |
string type; | |
aiString path; | |
}; | |
class Mesh { | |
public: | |
/* Mesh Data */ | |
vector<Vertex> vertices; | |
vector<GLuint> indices; | |
vector<Texture> textures; | |
GLuint VAO; | |
/* Functions */ | |
// Constructor | |
Mesh(vector<Vertex> vertices, vector<GLuint> indices, vector<Texture> textures) | |
{ | |
this->vertices = vertices; | |
this->indices = indices; | |
this->textures = textures; | |
// Now that we have all the required data, set the vertex buffers and its attribute pointers. | |
this->setupMesh(); | |
} | |
// Render the mesh | |
void Draw(Shader shader) | |
{ | |
// Bind appropriate textures | |
GLuint diffuseNr = 1; | |
GLuint specularNr = 1; | |
GLuint normalNr = 1; | |
GLuint heightNr = 1; | |
for (GLuint i = 0; i < this->textures.size(); i++) | |
{ | |
glActiveTexture(GL_TEXTURE0 + i); // Active proper texture unit before binding | |
// Retrieve texture number (the N in diffuse_textureN) | |
stringstream ss; | |
string number; | |
string name = this->textures[i].type; | |
if (name == "texture_diffuse") | |
ss << diffuseNr++; // Transfer GLuint to stream | |
else if (name == "texture_specular") | |
ss << specularNr++; // Transfer GLuint to stream | |
else if (name == "texture_normal") | |
ss << normalNr++; // Transfer GLuint to stream | |
else if (name == "texture_height") | |
ss << heightNr++; // Transfer GLuint to stream | |
number = ss.str(); | |
// Now set the sampler to the correct texture unit | |
glUniform1i(glGetUniformLocation(shader.ID, (name + number).c_str()), i); | |
// And finally bind the texture | |
glBindTexture(GL_TEXTURE_2D, this->textures[i].id); | |
} | |
// Draw mesh | |
glBindVertexArray(this->VAO); | |
glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0); | |
glBindVertexArray(0); | |
// Always good practice to set everything back to defaults once configured. | |
for (GLuint i = 0; i < this->textures.size(); i++) | |
{ | |
glActiveTexture(GL_TEXTURE0 + i); | |
glBindTexture(GL_TEXTURE_2D, 0); | |
} | |
} | |
private: | |
/* Render data */ | |
GLuint VBO, EBO; | |
/* Functions */ | |
// Initializes all the buffer objects/arrays | |
void setupMesh() | |
{ | |
// Create buffers/arrays | |
glGenVertexArrays(1, &this->VAO); | |
glGenBuffers(1, &this->VBO); | |
glGenBuffers(1, &this->EBO); | |
glBindVertexArray(this->VAO); | |
// Load data into vertex buffers | |
glBindBuffer(GL_ARRAY_BUFFER, this->VBO); | |
// A great thing about structs is that their memory layout is sequential for all its items. | |
// The effect is that we can simply pass a pointer to the struct and it translates perfectly to a glm::vec3/2 array which | |
// again translates to 3/2 floats which translates to a byte array. | |
glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(Vertex), &this->vertices[0], GL_STATIC_DRAW); | |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO); | |
glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->indices.size() * sizeof(GLuint), &this->indices[0], GL_STATIC_DRAW); | |
// Set the vertex attribute pointers | |
// Vertex Positions | |
glEnableVertexAttribArray(0); | |
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), static_cast<GLvoid*>(nullptr)); | |
// Vertex Normals | |
glEnableVertexAttribArray(1); | |
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<GLvoid*>(offsetof(Vertex, Normal))); | |
// Vertex Texture Coords | |
glEnableVertexAttribArray(2); | |
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<GLvoid*>(offsetof(Vertex, TexCoords))); | |
// Vertex Tangent | |
glEnableVertexAttribArray(3); | |
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<GLvoid*>(offsetof(Vertex, Tangent))); | |
// Vertex Bitangent | |
glEnableVertexAttribArray(4); | |
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<GLvoid*>(offsetof(Vertex, Bitangent))); | |
glBindVertexArray(0); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
// Std. Includes | |
#include <string> | |
#include <fstream> | |
#include <sstream> | |
#include <iostream> | |
#include <map> | |
#include <vector> | |
#include "shader.h" | |
using namespace std; | |
// GL Includes | |
#include <glad/glad.h> // Contains all the necessery OpenGL includes | |
#include <glm/glm.hpp> | |
#include <glm/gtc/matrix_transform.hpp> | |
#include <stb_image.h> | |
#include <assimp/Importer.hpp> | |
#include <assimp/scene.h> | |
#include <assimp/postprocess.h> | |
#include <mesh.h> | |
unsigned int TextureFromFile(const char* path, string directory, bool gamma = false); | |
class Model | |
{ | |
public: | |
/* Model Data */ | |
vector<Texture> textures_loaded; // Stores all the textures loaded so far, optimization to make sure textures aren't loaded more than once. | |
vector<Mesh> meshes; | |
string directory; | |
bool gammaCorrection; | |
/* Functions */ | |
// Constructor, expects a filepath to a 3D model. | |
Model(string const & path, bool gamma = false) : gammaCorrection(gamma) | |
{ | |
this->loadModel(path); | |
} | |
// Draws the model, and thus all its meshes | |
void Draw(Shader shader) | |
{ | |
for (GLuint i = 0; i < this->meshes.size(); i++) | |
this->meshes[i].Draw(shader); | |
} | |
private: | |
/* Functions */ | |
// Loads a model with supported ASSIMP extensions from file and stores the resulting meshes in the meshes vector. | |
void loadModel(string path) | |
{ | |
// Read file via ASSIMP | |
Assimp::Importer importer; | |
const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace); | |
// Check for errors | |
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero | |
{ | |
cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << endl; | |
return; | |
} | |
// Retrieve the directory path of the filepath | |
this->directory = path.substr(0, path.find_last_of('/')); | |
// Process ASSIMP's root node recursively | |
this->processNode(scene->mRootNode, scene); | |
} | |
// Processes a node in a recursive fashion. Processes each individual mesh located at the node and repeats this process on its children nodes (if any). | |
void processNode(aiNode* node, const aiScene* scene) | |
{ | |
// Process each mesh located at the current node | |
for (GLuint i = 0; i < node->mNumMeshes; i++) | |
{ | |
// The node object only contains indices to index the actual objects in the scene. | |
// The scene contains all the data, node is just to keep stuff organized (like relations between nodes). | |
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; | |
this->meshes.push_back(this->processMesh(mesh, scene)); | |
} | |
// After we've processed all of the meshes (if any) we then recursively process each of the children nodes | |
for (GLuint i = 0; i < node->mNumChildren; i++) | |
{ | |
this->processNode(node->mChildren[i], scene); | |
} | |
} | |
Mesh processMesh(aiMesh* mesh, const aiScene* scene) | |
{ | |
// Data to fill | |
vector<Vertex> vertices; | |
vector<GLuint> indices; | |
vector<Texture> textures; | |
// Walk through each of the mesh's vertices | |
for (GLuint i = 0; i < mesh->mNumVertices; i++) | |
{ | |
Vertex vertex; | |
glm::vec3 vector; // We declare a placeholder vector since assimp uses its own vector class that doesn't directly convert to glm's vec3 class so we transfer the data to this placeholder glm::vec3 first. | |
// Positions | |
vector.x = mesh->mVertices[i].x; | |
vector.y = mesh->mVertices[i].y; | |
vector.z = mesh->mVertices[i].z; | |
vertex.Position = vector; | |
// Normals | |
vector.x = mesh->mNormals[i].x; | |
vector.y = mesh->mNormals[i].y; | |
vector.z = mesh->mNormals[i].z; | |
vertex.Normal = vector; | |
// Texture Coordinates | |
if (mesh->mTextureCoords[0]) // Does the mesh contain texture coordinates? | |
{ | |
glm::vec2 vec; | |
// A vertex can contain up to 8 different texture coordinates. We thus make the assumption that we won't | |
// use models where a vertex can have multiple texture coordinates so we always take the first set (0). | |
vec.x = mesh->mTextureCoords[0][i].x; | |
vec.y = mesh->mTextureCoords[0][i].y; | |
vertex.TexCoords = vec; | |
} | |
else | |
vertex.TexCoords = glm::vec2(0.0f, 0.0f); | |
// Tangent | |
vector.x = mesh->mTangents[i].x; | |
vector.y = mesh->mTangents[i].y; | |
vector.z = mesh->mTangents[i].z; | |
vertex.Tangent = vector; | |
// Bitangent | |
vector.x = mesh->mBitangents[i].x; | |
vector.y = mesh->mBitangents[i].y; | |
vector.z = mesh->mBitangents[i].z; | |
vertex.Bitangent = vector; | |
vertices.push_back(vertex); | |
} | |
// Now wak through each of the mesh's faces (a face is a mesh its triangle) and retrieve the corresponding vertex indices. | |
for (GLuint i = 0; i < mesh->mNumFaces; i++) | |
{ | |
aiFace face = mesh->mFaces[i]; | |
// Retrieve all indices of the face and store them in the indices vector | |
for (GLuint j = 0; j < face.mNumIndices; j++) | |
indices.push_back(face.mIndices[j]); | |
} | |
// Process materials | |
if (mesh->mMaterialIndex >= 0) | |
{ | |
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex]; | |
// We assume a convention for sampler names in the shaders. Each diffuse texture should be named | |
// as 'texture_diffuseN' where N is a sequential number ranging from 1 to MAX_SAMPLER_NUMBER. | |
// Same applies to other texture as the following list summarizes: | |
// Diffuse: texture_diffuseN | |
// Specular: texture_specularN | |
// Normal: texture_normalN | |
// 1. Diffuse maps | |
vector<Texture> diffuseMaps = this->loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse"); | |
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end()); | |
// 2. Specular maps | |
vector<Texture> specularMaps = this->loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular"); | |
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); | |
// 3. Normal maps | |
std::vector<Texture> normalMaps = this->loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal"); | |
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end()); | |
// 4. Height maps | |
std::vector<Texture> heightMaps = this->loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_height"); | |
textures.insert(textures.end(), heightMaps.begin(), heightMaps.end()); | |
} | |
// Return a mesh object created from the extracted mesh data | |
return Mesh(vertices, indices, textures); | |
} | |
// Checks all material textures of a given type and loads the textures if they're not loaded yet. | |
// The required info is returned as a Texture struct. | |
vector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type, string typeName) | |
{ | |
vector<Texture> textures; | |
for (GLuint i = 0; i < mat->GetTextureCount(type); i++) | |
{ | |
aiString str; | |
mat->GetTexture(type, i, &str); | |
// Check if texture was loaded before and if so, continue to next iteration: skip loading a new texture | |
GLboolean skip = false; | |
for (GLuint j = 0; j < textures_loaded.size(); j++) | |
{ | |
if (std::strcmp(textures_loaded[j].path.C_Str(), str.C_Str()) == 0) | |
{ | |
textures.push_back(textures_loaded[j]); | |
skip = true; // A texture with the same filepath has already been loaded, continue to next one. (optimization) | |
break; | |
} | |
} | |
if (!skip) | |
{ // If texture hasn't been loaded already, load it | |
Texture texture; | |
texture.id = TextureFromFile(str.C_Str(), this->directory); | |
texture.type = typeName; | |
texture.path = str; | |
textures.push_back(texture); | |
this->textures_loaded.push_back(texture); // Store it as texture loaded for entire model, to ensure we won't unnecesery load duplicate textures. | |
} | |
} | |
return textures; | |
} | |
}; | |
unsigned int TextureFromFile(const char* path, string directory, bool gamma) | |
{ | |
string filename = string(path); | |
filename = directory + '/' + filename; | |
unsigned int textureID; | |
glGenTextures(1, &textureID); | |
int width, height, nrComponents; | |
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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <glad/glad.h> | |
#include <glm/glm.hpp> | |
#include <string> | |
#include <fstream> | |
#include <sstream> | |
#include <iostream> | |
class Shader | |
{ | |
public: | |
unsigned int ID; | |
// constructor generates the shader on the fly | |
// ------------------------------------------------------------------------ | |
Shader(const char* vertexPath, const char* fragmentPath) | |
{ | |
// 1. retrieve the vertex/fragment source code from filePath | |
std::string vertexCode; | |
std::string fragmentCode; | |
std::ifstream vShaderFile; | |
std::ifstream fShaderFile; | |
// ensure ifstream objects can throw exceptions: | |
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); | |
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); | |
try | |
{ | |
// open files | |
vShaderFile.open(vertexPath); | |
fShaderFile.open(fragmentPath); | |
std::stringstream vShaderStream, fShaderStream; | |
// read file's buffer contents into streams | |
vShaderStream << vShaderFile.rdbuf(); | |
fShaderStream << fShaderFile.rdbuf(); | |
// close file handlers | |
vShaderFile.close(); | |
fShaderFile.close(); | |
// convert stream into string | |
vertexCode = vShaderStream.str(); | |
fragmentCode = fShaderStream.str(); | |
} | |
catch (std::ifstream::failure e) | |
{ | |
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl; | |
} | |
const char* vShaderCode = vertexCode.c_str(); | |
const char * fShaderCode = fragmentCode.c_str(); | |
// 2. compile shaders | |
unsigned int vertex, fragment; | |
int success; | |
char infoLog[512]; | |
// vertex shader | |
vertex = glCreateShader(GL_VERTEX_SHADER); | |
glShaderSource(vertex, 1, &vShaderCode, NULL); | |
glCompileShader(vertex); | |
checkCompileErrors(vertex, "VERTEX"); | |
// fragment Shader | |
fragment = glCreateShader(GL_FRAGMENT_SHADER); | |
glShaderSource(fragment, 1, &fShaderCode, NULL); | |
glCompileShader(fragment); | |
checkCompileErrors(fragment, "FRAGMENT"); | |
// shader Program | |
ID = glCreateProgram(); | |
glAttachShader(ID, vertex); | |
glAttachShader(ID, fragment); | |
glLinkProgram(ID); | |
checkCompileErrors(ID, "PROGRAM"); | |
// delete the shaders as they're linked into our program now and no longer necessery | |
glDeleteShader(vertex); | |
glDeleteShader(fragment); | |
} | |
// activate the shader | |
// ------------------------------------------------------------------------ | |
void use() const | |
{ | |
glUseProgram(ID); | |
} | |
// utility uniform functions | |
// ------------------------------------------------------------------------ | |
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) const | |
{ | |
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: | |
// utility function for checking shader compilation/linking errors. | |
// ------------------------------------------------------------------------ | |
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; | |
} | |
} | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment