restructure v2
This commit is contained in:
parent
e01595e155
commit
dc5654ba9a
20 changed files with 910 additions and 791 deletions
|
|
@ -11,10 +11,11 @@ set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
main.cpp
|
main.cpp
|
||||||
game/game.cpp
|
game/game.cpp
|
||||||
engine/assets/modelLoader.cpp
|
engine/Assets/modelLoader.cpp
|
||||||
vendor/glad/glad.c
|
vendor/glad/glad.c
|
||||||
vendor/stb/stb_image.cpp
|
vendor/stb/stb_image.cpp
|
||||||
engine/Backend/Backend.cpp
|
engine/Backend/Backend.cpp
|
||||||
|
engine/Backend/Model.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
|
|
@ -24,7 +25,16 @@ set(HEADERS
|
||||||
engine/mesh.h
|
engine/mesh.h
|
||||||
engine/camera.h
|
engine/camera.h
|
||||||
game/game.h
|
game/game.h
|
||||||
engine/assets/modelLoader.h
|
engine/Assets/modelLoader.h
|
||||||
|
engine/Backend/Model.h
|
||||||
|
engine/Backend/Mesh.cpp
|
||||||
|
engine/Backend/Mesh.h
|
||||||
|
engine/Backend/Shader.cpp
|
||||||
|
engine/Backend/Shader.h
|
||||||
|
engine/Backend/Texture.cpp
|
||||||
|
engine/Backend/Texture.h
|
||||||
|
engine/Backend/Camera.cpp
|
||||||
|
engine/Backend/Camera.h
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create the executable
|
# Create the executable
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
|
||||||
#include "modelLoader.h"
|
#include "modelLoader.h"
|
||||||
|
|
||||||
namespace engine::assets
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace engine::Assets
|
||||||
{
|
{
|
||||||
Model loadBlendModel(const char *path)
|
Model loadBlendModel(const char *path)
|
||||||
{
|
{
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "engine/model.h"
|
#include "engine/Backend/Model.h"
|
||||||
|
|
||||||
namespace engine::assets
|
namespace engine::Assets
|
||||||
{
|
{
|
||||||
Model loadBlendModel(const char *path);
|
Model loadBlendModel(const char *path);
|
||||||
Model loadModel(const char *path);
|
Model loadModel(const char *path);
|
||||||
107
engine/Backend/Camera.cpp
Normal file
107
engine/Backend/Camera.cpp
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
|
||||||
|
#include "Camera.h"
|
||||||
|
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
// constructor with vectors
|
||||||
|
Camera::Camera(glm::vec3 newPosition, glm::vec3 up, float newYaw, float newPitch) : front(glm::vec3(0.0f, 0.0f, -1.0f)), movementSpeed(SPEED), mouseSensitivity(SENSITIVITY), zoom(ZOOM)
|
||||||
|
{
|
||||||
|
position = newPosition;
|
||||||
|
worldUp = up;
|
||||||
|
yaw = newYaw;
|
||||||
|
pitch = newPitch;
|
||||||
|
updateCameraVectors();
|
||||||
|
}
|
||||||
|
// constructor with scalar values
|
||||||
|
Camera::Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float newYaw, float newPitch) : front(glm::vec3(0.0f, 0.0f, -1.0f)), movementSpeed(SPEED), mouseSensitivity(SENSITIVITY), zoom(ZOOM)
|
||||||
|
{
|
||||||
|
position = glm::vec3(posX, posY, posZ);
|
||||||
|
worldUp = glm::vec3(upX, upY, upZ);
|
||||||
|
yaw = newYaw;
|
||||||
|
pitch = newPitch;
|
||||||
|
updateCameraVectors();
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the view matrix calculated using Euler Angles and the LookAt Matrix
|
||||||
|
glm::mat4 Camera::GetViewMatrix()
|
||||||
|
{
|
||||||
|
return glm::lookAt(position, position + front, up);
|
||||||
|
}
|
||||||
|
|
||||||
|
// processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
|
||||||
|
void Camera::processKeyboard(Camera_Movement direction, float deltaTime)
|
||||||
|
{
|
||||||
|
float velocity = movementSpeed * deltaTime;
|
||||||
|
if (sprinting)
|
||||||
|
velocity *= 8;
|
||||||
|
|
||||||
|
if (direction == FORWARD)
|
||||||
|
position += front * velocity;
|
||||||
|
if (direction == BACKWARD)
|
||||||
|
position -= front * velocity;
|
||||||
|
if (direction == LEFT)
|
||||||
|
position -= right * velocity;
|
||||||
|
if (direction == RIGHT)
|
||||||
|
position += right * velocity;
|
||||||
|
if (direction == SPRINT)
|
||||||
|
sprinting = !sprinting;
|
||||||
|
}
|
||||||
|
|
||||||
|
// processes input received from a mouse input system. Expects the offset value in both the x and y direction.
|
||||||
|
void Camera::processMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch)
|
||||||
|
{
|
||||||
|
xoffset *= mouseSensitivity;
|
||||||
|
yoffset *= mouseSensitivity;
|
||||||
|
|
||||||
|
yaw += xoffset;
|
||||||
|
pitch += yoffset;
|
||||||
|
|
||||||
|
// make sure that when pitch is out of bounds, screen doesn't get flipped
|
||||||
|
if (constrainPitch)
|
||||||
|
{
|
||||||
|
if (pitch > 89.0f)
|
||||||
|
pitch = 89.0f;
|
||||||
|
|
||||||
|
if (pitch < -89.0f)
|
||||||
|
pitch = -89.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update Front, Right and Up Vectors using the updated Euler angles
|
||||||
|
updateCameraVectors();
|
||||||
|
}
|
||||||
|
|
||||||
|
// processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
|
||||||
|
void Camera::processMouseScroll(float yoffset)
|
||||||
|
{
|
||||||
|
zoom -= (float)yoffset;
|
||||||
|
if (zoom < 1.0f)
|
||||||
|
zoom = 1.0f;
|
||||||
|
if (zoom > 45.0f)
|
||||||
|
zoom = 45.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::Update(Shader shader, int screenWidth, int screenHeight)
|
||||||
|
{
|
||||||
|
glm::mat4 projection = glm::perspective(glm::radians(zoom), (float)screenWidth / (float)screenHeight, 0.1f, 100.0f);
|
||||||
|
|
||||||
|
shader.setMat4("projection", projection);
|
||||||
|
shader.setMat4("view", GetViewMatrix());
|
||||||
|
shader.setVec3("viewPos", position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculates the front vector from the Camera's (updated) Euler Angles
|
||||||
|
void Camera::updateCameraVectors()
|
||||||
|
{
|
||||||
|
// calculate the new Front vector
|
||||||
|
glm::vec3 newFront;
|
||||||
|
newFront.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
|
||||||
|
newFront.y = sin(glm::radians(pitch));
|
||||||
|
newFront.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
|
||||||
|
front = glm::normalize(newFront);
|
||||||
|
|
||||||
|
// also re-calculate the Right and Up vector
|
||||||
|
right = glm::normalize(glm::cross(front, worldUp)); // normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
|
||||||
|
up = glm::normalize(glm::cross(right, front));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
51
engine/Backend/Camera.h
Normal file
51
engine/Backend/Camera.h
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Shader.h"
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
// Movement keys/buttons
|
||||||
|
enum Camera_Movement {
|
||||||
|
FORWARD,
|
||||||
|
BACKWARD,
|
||||||
|
LEFT,
|
||||||
|
RIGHT,
|
||||||
|
SPRINT
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default camera values
|
||||||
|
const float YAW = -90.0f;
|
||||||
|
const float PITCH = 0.0f;
|
||||||
|
const float SPEED = 5.5f;
|
||||||
|
const float SENSITIVITY = 0.1f;
|
||||||
|
const float ZOOM = 45.0f;
|
||||||
|
|
||||||
|
class Camera
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
glm::vec3 position, front, up, right, worldUp;
|
||||||
|
float yaw, pitch;
|
||||||
|
float movementSpeed, mouseSensitivity, zoom;
|
||||||
|
bool sprinting = false;
|
||||||
|
|
||||||
|
// constructor with vectors
|
||||||
|
Camera(glm::vec3 newPosition = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float newYaw = YAW, float newPitch = PITCH); // constructor with scalar values
|
||||||
|
Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float newYaw, float newPitch);
|
||||||
|
// returns the view matrix calculated using Euler Angles and the LookAt Matrix
|
||||||
|
glm::mat4 GetViewMatrix();
|
||||||
|
// processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
|
||||||
|
void processKeyboard(Camera_Movement direction, float deltaTime);
|
||||||
|
// processes input received from a mouse input system. Expects the offset value in both the x and y direction.
|
||||||
|
void processMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true);
|
||||||
|
|
||||||
|
// processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
|
||||||
|
void processMouseScroll(float yoffset);
|
||||||
|
void Update(Shader shader, int screenWidth, int screenHeight);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// calculates the front vector from the Camera's (updated) Euler Angles
|
||||||
|
void updateCameraVectors();
|
||||||
|
};
|
||||||
|
|
||||||
101
engine/Backend/Mesh.cpp
Normal file
101
engine/Backend/Mesh.cpp
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
|
||||||
|
#include "Mesh.h"
|
||||||
|
|
||||||
|
#include <glad/glad.h> // holds all OpenGL type declarations
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
Mesh::Mesh(vector<Vertex> vertices, vector<unsigned int> 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.
|
||||||
|
setupMesh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// render the mesh
|
||||||
|
void Mesh::Draw(Shader &shader)
|
||||||
|
{
|
||||||
|
// bind appropriate textures
|
||||||
|
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); // active proper texture unit before binding
|
||||||
|
// retrieve texture number (the N in diffuse_textureN)
|
||||||
|
string number;
|
||||||
|
string name = textures[i].type;
|
||||||
|
if(name == "texture_diffuse")
|
||||||
|
number = std::to_string(diffuseNr++);
|
||||||
|
else if(name == "texture_specular")
|
||||||
|
number = std::to_string(specularNr++); // transfer unsigned int to string
|
||||||
|
else if(name == "texture_normal")
|
||||||
|
number = std::to_string(normalNr++); // transfer unsigned int to string
|
||||||
|
else if(name == "texture_height")
|
||||||
|
number = std::to_string(heightNr++); // transfer unsigned int to string
|
||||||
|
|
||||||
|
// 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, textures[i].id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw mesh
|
||||||
|
glBindVertexArray(VAO);
|
||||||
|
glDrawElements(GL_TRIANGLES, static_cast<unsigned int>(indices.size()), GL_UNSIGNED_INT, 0);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
// always good practice to set everything back to defaults once configured.
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// initializes all the buffer objects/arrays
|
||||||
|
void Mesh::setupMesh()
|
||||||
|
{
|
||||||
|
// create buffers/arrays
|
||||||
|
glGenVertexArrays(1, &VAO);
|
||||||
|
glGenBuffers(1, &VBO);
|
||||||
|
glGenBuffers(1, &EBO);
|
||||||
|
|
||||||
|
glBindVertexArray(VAO);
|
||||||
|
// load data into vertex buffers
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 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, 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);
|
||||||
|
|
||||||
|
// set the vertex attribute pointers
|
||||||
|
// vertex Positions
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
|
||||||
|
// vertex normals
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
|
||||||
|
// vertex texture coords
|
||||||
|
glEnableVertexAttribArray(2);
|
||||||
|
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));
|
||||||
|
// vertex tangent
|
||||||
|
glEnableVertexAttribArray(3);
|
||||||
|
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Tangent));
|
||||||
|
// vertex bitangent
|
||||||
|
glEnableVertexAttribArray(4);
|
||||||
|
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Bitangent));
|
||||||
|
// ids
|
||||||
|
glEnableVertexAttribArray(5);
|
||||||
|
glVertexAttribIPointer(5, 4, GL_INT, sizeof(Vertex), (void*)offsetof(Vertex, m_BoneIDs));
|
||||||
|
|
||||||
|
// weights
|
||||||
|
glEnableVertexAttribArray(6);
|
||||||
|
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, m_Weights));
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
50
engine/Backend/Mesh.h
Normal file
50
engine/Backend/Mesh.h
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <engine/Backend/Shader.h>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#define MAX_BONE_INFLUENCE 4
|
||||||
|
|
||||||
|
struct Vertex {
|
||||||
|
// position
|
||||||
|
glm::vec3 Position;
|
||||||
|
// normal
|
||||||
|
glm::vec3 Normal;
|
||||||
|
// texCoords
|
||||||
|
glm::vec2 TexCoords;
|
||||||
|
// tangent
|
||||||
|
glm::vec3 Tangent;
|
||||||
|
// bitangent
|
||||||
|
glm::vec3 Bitangent;
|
||||||
|
//bone indexes which will influence this vertex
|
||||||
|
int m_BoneIDs[MAX_BONE_INFLUENCE];
|
||||||
|
//weights from each bone
|
||||||
|
float m_Weights[MAX_BONE_INFLUENCE];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Texture {
|
||||||
|
unsigned int id;
|
||||||
|
string type;
|
||||||
|
string path;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Mesh {
|
||||||
|
public:
|
||||||
|
// mesh Data
|
||||||
|
vector<Vertex> vertices;
|
||||||
|
vector<unsigned int> indices;
|
||||||
|
vector<Texture> textures;
|
||||||
|
unsigned int VAO;
|
||||||
|
|
||||||
|
Mesh(vector<Vertex> vertices, vector<unsigned int> indices, vector<Texture> textures);
|
||||||
|
void Draw(Shader &shader);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// render data
|
||||||
|
unsigned int VBO, EBO;
|
||||||
|
|
||||||
|
// initializes all the buffer objects/arrays
|
||||||
|
void setupMesh();
|
||||||
|
};
|
||||||
240
engine/Backend/Model.cpp
Normal file
240
engine/Backend/Model.cpp
Normal file
|
|
@ -0,0 +1,240 @@
|
||||||
|
|
||||||
|
#include "Model.h"
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
#include <stb/stb_image.h>
|
||||||
|
#include <assimp/postprocess.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <filesystem>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
// constructor, expects a filepath to a 3D model.
|
||||||
|
Model::Model(string const &path, bool gamma) : gammaCorrection(gamma)
|
||||||
|
{
|
||||||
|
loadModel(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// draws the model, and thus all its meshes
|
||||||
|
void Model::Draw(Shader &shader)
|
||||||
|
{
|
||||||
|
for(unsigned int i = 0; i < meshes.size(); i++)
|
||||||
|
meshes[i].Draw(shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
// loads a model with supported ASSIMP extensions from file and stores the resulting meshes in the meshes vector.
|
||||||
|
void Model::loadModel(string const &path)
|
||||||
|
{
|
||||||
|
// read file via ASSIMP
|
||||||
|
Assimp::Importer importer;
|
||||||
|
const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_GenSmoothNormals | 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
|
||||||
|
directory = path.substr(0, path.find_last_of('/'));
|
||||||
|
|
||||||
|
// process ASSIMP's root node recursively
|
||||||
|
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 Model::processNode(aiNode *node, const aiScene *scene)
|
||||||
|
{
|
||||||
|
// process each mesh located at the current node
|
||||||
|
for(unsigned int 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]];
|
||||||
|
meshes.push_back(processMesh(mesh, scene));
|
||||||
|
}
|
||||||
|
// after we've processed all of the meshes (if any) we then recursively process each of the children nodes
|
||||||
|
for(unsigned int i = 0; i < node->mNumChildren; i++)
|
||||||
|
{
|
||||||
|
processNode(node->mChildren[i], scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene)
|
||||||
|
{
|
||||||
|
// data to fill
|
||||||
|
vector<Vertex> vertices;
|
||||||
|
vector<unsigned int> indices;
|
||||||
|
vector<Texture> textures;
|
||||||
|
|
||||||
|
// walk through each of the mesh's vertices
|
||||||
|
for(unsigned int 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
|
||||||
|
if (mesh->HasNormals())
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
vertex.TexCoords = glm::vec2(0.0f, 0.0f);
|
||||||
|
|
||||||
|
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(unsigned int 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(unsigned int j = 0; j < face.mNumIndices; j++)
|
||||||
|
indices.push_back(face.mIndices[j]);
|
||||||
|
}
|
||||||
|
// process materials
|
||||||
|
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 = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse", true);
|
||||||
|
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
|
||||||
|
// 2. specular maps
|
||||||
|
vector<Texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
|
||||||
|
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
|
||||||
|
// 3. normal maps
|
||||||
|
std::vector<Texture> normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal");
|
||||||
|
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
|
||||||
|
// 4. height maps
|
||||||
|
std::vector<Texture> heightMaps = 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> Model::loadMaterialTextures(aiMaterial *mat, aiTextureType type, string typeName, bool fallback)
|
||||||
|
{
|
||||||
|
vector<Texture> textures;
|
||||||
|
for(unsigned int 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
|
||||||
|
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; // 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.C_Str();
|
||||||
|
textures.push_back(texture);
|
||||||
|
textures_loaded.push_back(texture); // store it as texture loaded for entire model, to ensure we won't unnecessary load duplicate textures.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load DefaultTexture if there isn't one already
|
||||||
|
if (mat->GetTextureCount(type) == 0) {
|
||||||
|
string defaultPath = "DefaultTexture.jpg";
|
||||||
|
|
||||||
|
Texture texture;
|
||||||
|
texture.id = TextureFromFile(defaultPath.c_str(), "assets/");
|
||||||
|
texture.type = typeName;
|
||||||
|
texture.path = defaultPath;
|
||||||
|
textures.push_back(texture);
|
||||||
|
textures_loaded.push_back(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
return textures;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
unsigned inline int TextureFromFile(const char *path, const 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;
|
||||||
|
}
|
||||||
39
engine/Backend/Model.h
Normal file
39
engine/Backend/Model.h
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assimp/Importer.hpp>
|
||||||
|
#include <assimp/scene.h>
|
||||||
|
|
||||||
|
#include <engine/Backend/Shader.h>
|
||||||
|
#include <engine/Backend/Mesh.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// constructor, expects a filepath to a 3D model.
|
||||||
|
Model(string const &path, bool gamma = false);
|
||||||
|
|
||||||
|
// draws the model, and thus all its meshes
|
||||||
|
void Draw(Shader &shader);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// loads a model with supported ASSIMP extensions from file and stores the resulting meshes in the meshes vector.
|
||||||
|
void loadModel(string const &path);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
Mesh processMesh(aiMesh *mesh, const aiScene *scene);
|
||||||
|
|
||||||
|
// 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, bool fallback = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned inline int TextureFromFile(const char *path, const string &directory, bool gamma = false);
|
||||||
176
engine/Backend/Shader.cpp
Normal file
176
engine/Backend/Shader.cpp
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
|
||||||
|
#include "Shader.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
Shader::Shader(const char* vertexPath, const char* fragmentPath)
|
||||||
|
{
|
||||||
|
std::string vertexCode, fragmentCode;
|
||||||
|
std::ifstream vertexFile, fragmentFile;
|
||||||
|
|
||||||
|
vertexFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
|
||||||
|
fragmentFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Open the files
|
||||||
|
vertexFile.open(vertexPath);
|
||||||
|
fragmentFile.open(fragmentPath);
|
||||||
|
std::stringstream vertexStream, fragmentStream;
|
||||||
|
|
||||||
|
// Save the contents to our streams
|
||||||
|
vertexStream << vertexFile.rdbuf();
|
||||||
|
fragmentStream << fragmentFile.rdbuf();
|
||||||
|
|
||||||
|
// Close files
|
||||||
|
vertexFile.close();
|
||||||
|
fragmentFile.close();
|
||||||
|
|
||||||
|
// Save the contents to our strings
|
||||||
|
vertexCode = vertexStream.str();
|
||||||
|
fragmentCode = fragmentStream.str();
|
||||||
|
|
||||||
|
// Fix encoding issues, specifically for Linux
|
||||||
|
//vertexCode.erase(std::remove(vertexCode.begin(), vertexCode.end(), '\r'), vertexCode.end());
|
||||||
|
//fragmentCode.erase(std::remove(fragmentCode.begin(), fragmentCode.end(), '\r'), fragmentCode.end());
|
||||||
|
}
|
||||||
|
catch (std::ifstream::failure& e)
|
||||||
|
{
|
||||||
|
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* vertexShaderSource = vertexCode.c_str();
|
||||||
|
const char* fragmentShaderSource = fragmentCode.c_str();
|
||||||
|
|
||||||
|
unsigned int vertexShader;
|
||||||
|
vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
||||||
|
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
|
||||||
|
glCompileShader(vertexShader);
|
||||||
|
checkCompileErrors(vertexShader, "VERTEX");
|
||||||
|
|
||||||
|
unsigned int fragmentShader;
|
||||||
|
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
|
||||||
|
glCompileShader(fragmentShader);
|
||||||
|
checkCompileErrors(fragmentShader, "FRAGMENT");
|
||||||
|
|
||||||
|
ID = glCreateProgram();
|
||||||
|
glAttachShader(ID, vertexShader);
|
||||||
|
glAttachShader(ID, fragmentShader);
|
||||||
|
glLinkProgram(ID);
|
||||||
|
checkCompileErrors(ID, "PROGRAM");
|
||||||
|
|
||||||
|
glDeleteShader(vertexShader);
|
||||||
|
glDeleteShader(fragmentShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shader::Use() const
|
||||||
|
{
|
||||||
|
glUseProgram(ID);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Setters
|
||||||
|
void Shader::setBool(const std::string &name, bool value) const
|
||||||
|
{
|
||||||
|
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void Shader::setInt(const std::string &name, int value) const
|
||||||
|
{
|
||||||
|
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void Shader::setFloat(const std::string &name, float value) const
|
||||||
|
{
|
||||||
|
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void Shader::setVec2(const std::string &name, const glm::vec2 &value) const
|
||||||
|
{
|
||||||
|
glUniform2fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
|
||||||
|
}
|
||||||
|
void Shader::setVec2(const std::string &name, float x, float y) const
|
||||||
|
{
|
||||||
|
glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void Shader::setVec3(const std::string &name, const glm::vec3 &value) const
|
||||||
|
{
|
||||||
|
glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
|
||||||
|
}
|
||||||
|
void Shader::setVec3(const std::string &name, float x, float y, float z) const
|
||||||
|
{
|
||||||
|
glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void Shader::setArr(const std::string &name, const GLint value[], const int &count) const
|
||||||
|
{
|
||||||
|
glUniform1iv(glGetUniformLocation(ID, name.c_str()), count, value);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void Shader::setVec4(const std::string &name, const glm::vec4 &value) const
|
||||||
|
{
|
||||||
|
glUniform4fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
|
||||||
|
}
|
||||||
|
void Shader::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 Shader::setMat2(const std::string &name, const glm::mat2 &mat) const
|
||||||
|
{
|
||||||
|
glUniformMatrix2fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void Shader::setMat3(const std::string &name, const glm::mat3 &mat) const
|
||||||
|
{
|
||||||
|
glUniformMatrix3fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void Shader::setMat4(const std::string &name, const glm::mat4 &mat) const
|
||||||
|
{
|
||||||
|
glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shader::setDecal(int index, GLint tex, float opacity, const glm::vec4& uvCoords) const {
|
||||||
|
std::string indexStr = std::to_string(index);
|
||||||
|
|
||||||
|
GLint texLoc = glGetUniformLocation(ID, ("decals[" + indexStr + "].tex").c_str());
|
||||||
|
if (texLoc != -1) glUniform1i(texLoc, tex);
|
||||||
|
|
||||||
|
GLint opLoc = glGetUniformLocation(ID, ("decals[" + indexStr + "].opacity").c_str());
|
||||||
|
if (opLoc != -1) glUniform1f(opLoc, opacity);
|
||||||
|
|
||||||
|
GLint uvLoc = glGetUniformLocation(ID, ("decals[" + indexStr + "].uvCoords").c_str());
|
||||||
|
if (uvLoc != -1) {
|
||||||
|
glUniform4fv(uvLoc, 1, &uvCoords[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shader::checkCompileErrors(unsigned int shader, std::string type)
|
||||||
|
{
|
||||||
|
int success;
|
||||||
|
char 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
34
engine/Backend/Shader.h
Normal file
34
engine/Backend/Shader.h
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Shader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
unsigned int ID;
|
||||||
|
|
||||||
|
Shader(const char* vertexPath, const char* fragmentPath);
|
||||||
|
void Use() const;
|
||||||
|
|
||||||
|
// Setters;
|
||||||
|
void setBool(const std::string &name, bool value) const;
|
||||||
|
void setInt(const std::string &name, int value) const;
|
||||||
|
void setFloat(const std::string &name, float value) const;
|
||||||
|
void setVec2(const std::string &name, const glm::vec2 &value) const;
|
||||||
|
void setVec2(const std::string &name, float x, float y) const;
|
||||||
|
void setVec3(const std::string &name, const glm::vec3 &value) const;
|
||||||
|
void setVec3(const std::string &name, float x, float y, float z) const;
|
||||||
|
void setArr(const std::string &name, const GLint value[], const int &count) const;
|
||||||
|
void setVec4(const std::string &name, const glm::vec4 &value) const;
|
||||||
|
void setVec4(const std::string &name, float x, float y, float z, float w) const;
|
||||||
|
void setMat2(const std::string &name, const glm::mat2 &mat) const;
|
||||||
|
void setMat3(const std::string &name, const glm::mat3 &mat) const;
|
||||||
|
void setMat4(const std::string &name, const glm::mat4 &mat) const;
|
||||||
|
void setDecal(int index, GLint tex, float opacity, const glm::vec4& uvCoords) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void checkCompileErrors(unsigned int shader, std::string type);
|
||||||
|
};
|
||||||
47
engine/Backend/Texture.cpp
Normal file
47
engine/Backend/Texture.cpp
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
|
||||||
|
#include "Texture.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include <stb/stb_image.h>
|
||||||
|
|
||||||
|
Texture::Texture(const char* texturePath, bool flip)
|
||||||
|
{
|
||||||
|
// Create the Texture and set our ID
|
||||||
|
glGenTextures(1, &ID);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, ID);
|
||||||
|
|
||||||
|
// set the texture wrapping/filtering options (on the currently bound texture object)
|
||||||
|
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_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
|
// Grab the texture from our path
|
||||||
|
if (flip)
|
||||||
|
stbi_set_flip_vertically_on_load(true);
|
||||||
|
|
||||||
|
unsigned char *data = stbi_load(texturePath, &width, &height, &nrChannels, 0);
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
GLenum format = (nrChannels == 4) ? GL_RGBA : GL_RGB;
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "Failed to load texture" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flip)
|
||||||
|
stbi_set_flip_vertically_on_load(false);
|
||||||
|
|
||||||
|
stbi_image_free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::Draw() const
|
||||||
|
{
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, ID);
|
||||||
|
}
|
||||||
12
engine/Backend/Texture.h
Normal file
12
engine/Backend/Texture.h
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class Texture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
unsigned int ID;
|
||||||
|
int width, height, nrChannels;
|
||||||
|
|
||||||
|
Texture(const char* texturePath, bool flip = false);
|
||||||
|
void Draw() const;
|
||||||
|
};
|
||||||
136
engine/camera.h
136
engine/camera.h
|
|
@ -1,136 +0,0 @@
|
||||||
|
|
||||||
#ifndef ENGINE2026_CAMERA_H
|
|
||||||
#define ENGINE2026_CAMERA_H
|
|
||||||
|
|
||||||
#include <glad/glad.h>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
|
||||||
|
|
||||||
// Movement keys/buttons
|
|
||||||
enum Camera_Movement {
|
|
||||||
FORWARD,
|
|
||||||
BACKWARD,
|
|
||||||
LEFT,
|
|
||||||
RIGHT,
|
|
||||||
SPRINT
|
|
||||||
};
|
|
||||||
|
|
||||||
// Default camera values
|
|
||||||
const float YAW = -90.0f;
|
|
||||||
const float PITCH = 0.0f;
|
|
||||||
const float SPEED = 5.5f;
|
|
||||||
const float SENSITIVITY = 0.1f;
|
|
||||||
const float ZOOM = 45.0f;
|
|
||||||
|
|
||||||
class Camera
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
glm::vec3 position, front, up, right, worldUp;
|
|
||||||
float yaw, pitch;
|
|
||||||
float movementSpeed, mouseSensitivity, zoom;
|
|
||||||
bool sprinting = false;
|
|
||||||
|
|
||||||
// constructor with vectors
|
|
||||||
Camera(glm::vec3 newPosition = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float newYaw = YAW, float newPitch = PITCH) : front(glm::vec3(0.0f, 0.0f, -1.0f)), movementSpeed(SPEED), mouseSensitivity(SENSITIVITY), zoom(ZOOM)
|
|
||||||
{
|
|
||||||
position = newPosition;
|
|
||||||
worldUp = up;
|
|
||||||
yaw = newYaw;
|
|
||||||
pitch = newPitch;
|
|
||||||
updateCameraVectors();
|
|
||||||
}
|
|
||||||
// constructor with scalar values
|
|
||||||
Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float newYaw, float newPitch) : front(glm::vec3(0.0f, 0.0f, -1.0f)), movementSpeed(SPEED), mouseSensitivity(SENSITIVITY), zoom(ZOOM)
|
|
||||||
{
|
|
||||||
position = glm::vec3(posX, posY, posZ);
|
|
||||||
worldUp = glm::vec3(upX, upY, upZ);
|
|
||||||
yaw = newYaw;
|
|
||||||
pitch = newPitch;
|
|
||||||
updateCameraVectors();
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the view matrix calculated using Euler Angles and the LookAt Matrix
|
|
||||||
glm::mat4 GetViewMatrix()
|
|
||||||
{
|
|
||||||
return glm::lookAt(position, position + front, up);
|
|
||||||
}
|
|
||||||
|
|
||||||
// processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
|
|
||||||
void processKeyboard(Camera_Movement direction, float deltaTime)
|
|
||||||
{
|
|
||||||
float velocity = movementSpeed * deltaTime;
|
|
||||||
if (sprinting)
|
|
||||||
velocity *= 8;
|
|
||||||
|
|
||||||
if (direction == FORWARD)
|
|
||||||
position += front * velocity;
|
|
||||||
if (direction == BACKWARD)
|
|
||||||
position -= front * velocity;
|
|
||||||
if (direction == LEFT)
|
|
||||||
position -= right * velocity;
|
|
||||||
if (direction == RIGHT)
|
|
||||||
position += right * velocity;
|
|
||||||
if (direction == SPRINT)
|
|
||||||
sprinting = !sprinting;
|
|
||||||
}
|
|
||||||
|
|
||||||
// processes input received from a mouse input system. Expects the offset value in both the x and y direction.
|
|
||||||
void processMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true)
|
|
||||||
{
|
|
||||||
xoffset *= mouseSensitivity;
|
|
||||||
yoffset *= mouseSensitivity;
|
|
||||||
|
|
||||||
yaw += xoffset;
|
|
||||||
pitch += yoffset;
|
|
||||||
|
|
||||||
// make sure that when pitch is out of bounds, screen doesn't get flipped
|
|
||||||
if (constrainPitch)
|
|
||||||
{
|
|
||||||
if (pitch > 89.0f)
|
|
||||||
pitch = 89.0f;
|
|
||||||
|
|
||||||
if (pitch < -89.0f)
|
|
||||||
pitch = -89.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update Front, Right and Up Vectors using the updated Euler angles
|
|
||||||
updateCameraVectors();
|
|
||||||
}
|
|
||||||
|
|
||||||
// processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
|
|
||||||
void processMouseScroll(float yoffset)
|
|
||||||
{
|
|
||||||
zoom -= (float)yoffset;
|
|
||||||
if (zoom < 1.0f)
|
|
||||||
zoom = 1.0f;
|
|
||||||
if (zoom > 45.0f)
|
|
||||||
zoom = 45.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Update(Shader shader, int screenWidth, int screenHeight)
|
|
||||||
{
|
|
||||||
glm::mat4 projection = glm::perspective(glm::radians(zoom), (float)screenWidth / (float)screenHeight, 0.1f, 100.0f);
|
|
||||||
|
|
||||||
shader.setMat4("projection", projection);
|
|
||||||
shader.setMat4("view", GetViewMatrix());
|
|
||||||
shader.setVec3("viewPos", position);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// calculates the front vector from the Camera's (updated) Euler Angles
|
|
||||||
void updateCameraVectors()
|
|
||||||
{
|
|
||||||
// calculate the new Front vector
|
|
||||||
glm::vec3 newFront;
|
|
||||||
newFront.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
|
|
||||||
newFront.y = sin(glm::radians(pitch));
|
|
||||||
newFront.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
|
|
||||||
front = glm::normalize(newFront);
|
|
||||||
|
|
||||||
// also re-calculate the Right and Up vector
|
|
||||||
right = glm::normalize(glm::cross(front, worldUp)); // normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
|
|
||||||
up = glm::normalize(glm::cross(right, front));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
147
engine/mesh.h
147
engine/mesh.h
|
|
@ -1,147 +0,0 @@
|
||||||
|
|
||||||
#ifndef ENGINE2026_MESH_H
|
|
||||||
#define ENGINE2026_MESH_H
|
|
||||||
|
|
||||||
#include <glad/glad.h> // holds all OpenGL type declarations
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
|
||||||
|
|
||||||
#include <engine/shader.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#define MAX_BONE_INFLUENCE 4
|
|
||||||
|
|
||||||
struct Vertex {
|
|
||||||
// position
|
|
||||||
glm::vec3 Position;
|
|
||||||
// normal
|
|
||||||
glm::vec3 Normal;
|
|
||||||
// texCoords
|
|
||||||
glm::vec2 TexCoords;
|
|
||||||
// tangent
|
|
||||||
glm::vec3 Tangent;
|
|
||||||
// bitangent
|
|
||||||
glm::vec3 Bitangent;
|
|
||||||
//bone indexes which will influence this vertex
|
|
||||||
int m_BoneIDs[MAX_BONE_INFLUENCE];
|
|
||||||
//weights from each bone
|
|
||||||
float m_Weights[MAX_BONE_INFLUENCE];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Texture {
|
|
||||||
unsigned int id;
|
|
||||||
string type;
|
|
||||||
string path;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Mesh {
|
|
||||||
public:
|
|
||||||
// mesh Data
|
|
||||||
vector<Vertex> vertices;
|
|
||||||
vector<unsigned int> indices;
|
|
||||||
vector<Texture> textures;
|
|
||||||
unsigned int VAO;
|
|
||||||
|
|
||||||
// constructor
|
|
||||||
Mesh(vector<Vertex> vertices, vector<unsigned int> 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.
|
|
||||||
setupMesh();
|
|
||||||
}
|
|
||||||
|
|
||||||
// render the mesh
|
|
||||||
void Draw(Shader &shader)
|
|
||||||
{
|
|
||||||
// bind appropriate textures
|
|
||||||
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); // active proper texture unit before binding
|
|
||||||
// retrieve texture number (the N in diffuse_textureN)
|
|
||||||
string number;
|
|
||||||
string name = textures[i].type;
|
|
||||||
if(name == "texture_diffuse")
|
|
||||||
number = std::to_string(diffuseNr++);
|
|
||||||
else if(name == "texture_specular")
|
|
||||||
number = std::to_string(specularNr++); // transfer unsigned int to string
|
|
||||||
else if(name == "texture_normal")
|
|
||||||
number = std::to_string(normalNr++); // transfer unsigned int to string
|
|
||||||
else if(name == "texture_height")
|
|
||||||
number = std::to_string(heightNr++); // transfer unsigned int to string
|
|
||||||
|
|
||||||
// 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, textures[i].id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw mesh
|
|
||||||
glBindVertexArray(VAO);
|
|
||||||
glDrawElements(GL_TRIANGLES, static_cast<unsigned int>(indices.size()), GL_UNSIGNED_INT, 0);
|
|
||||||
glBindVertexArray(0);
|
|
||||||
|
|
||||||
// always good practice to set everything back to defaults once configured.
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// render data
|
|
||||||
unsigned int VBO, EBO;
|
|
||||||
|
|
||||||
// initializes all the buffer objects/arrays
|
|
||||||
void setupMesh()
|
|
||||||
{
|
|
||||||
// create buffers/arrays
|
|
||||||
glGenVertexArrays(1, &VAO);
|
|
||||||
glGenBuffers(1, &VBO);
|
|
||||||
glGenBuffers(1, &EBO);
|
|
||||||
|
|
||||||
glBindVertexArray(VAO);
|
|
||||||
// load data into vertex buffers
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 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, 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);
|
|
||||||
|
|
||||||
// set the vertex attribute pointers
|
|
||||||
// vertex Positions
|
|
||||||
glEnableVertexAttribArray(0);
|
|
||||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
|
|
||||||
// vertex normals
|
|
||||||
glEnableVertexAttribArray(1);
|
|
||||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal));
|
|
||||||
// vertex texture coords
|
|
||||||
glEnableVertexAttribArray(2);
|
|
||||||
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords));
|
|
||||||
// vertex tangent
|
|
||||||
glEnableVertexAttribArray(3);
|
|
||||||
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Tangent));
|
|
||||||
// vertex bitangent
|
|
||||||
glEnableVertexAttribArray(4);
|
|
||||||
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Bitangent));
|
|
||||||
// ids
|
|
||||||
glEnableVertexAttribArray(5);
|
|
||||||
glVertexAttribIPointer(5, 4, GL_INT, sizeof(Vertex), (void*)offsetof(Vertex, m_BoneIDs));
|
|
||||||
|
|
||||||
// weights
|
|
||||||
glEnableVertexAttribArray(6);
|
|
||||||
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, m_Weights));
|
|
||||||
glBindVertexArray(0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif //ENGINE2026_MESH_H
|
|
||||||
260
engine/model.h
260
engine/model.h
|
|
@ -1,260 +0,0 @@
|
||||||
|
|
||||||
#ifndef ENGINE2026_MODEL_H
|
|
||||||
#define ENGINE2026_MODEL_H
|
|
||||||
|
|
||||||
#include <glad/glad.h>
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
|
||||||
#include <stb/stb_image.h>
|
|
||||||
#include <assimp/Importer.hpp>
|
|
||||||
#include <assimp/scene.h>
|
|
||||||
#include <assimp/postprocess.h>
|
|
||||||
|
|
||||||
#include <engine/mesh.h>
|
|
||||||
#include <engine/shader.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <filesystem>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
unsigned int TextureFromFile(const char *path, const 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;
|
|
||||||
|
|
||||||
// constructor, expects a filepath to a 3D model.
|
|
||||||
Model(string const &path, bool gamma = false) : gammaCorrection(gamma)
|
|
||||||
{
|
|
||||||
loadModel(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// draws the model, and thus all its meshes
|
|
||||||
void Draw(Shader &shader)
|
|
||||||
{
|
|
||||||
for(unsigned int i = 0; i < meshes.size(); i++)
|
|
||||||
meshes[i].Draw(shader);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// loads a model with supported ASSIMP extensions from file and stores the resulting meshes in the meshes vector.
|
|
||||||
void loadModel(string const &path)
|
|
||||||
{
|
|
||||||
// read file via ASSIMP
|
|
||||||
Assimp::Importer importer;
|
|
||||||
const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_GenSmoothNormals | 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
|
|
||||||
directory = path.substr(0, path.find_last_of('/'));
|
|
||||||
|
|
||||||
// process ASSIMP's root node recursively
|
|
||||||
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(unsigned int 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]];
|
|
||||||
meshes.push_back(processMesh(mesh, scene));
|
|
||||||
}
|
|
||||||
// after we've processed all of the meshes (if any) we then recursively process each of the children nodes
|
|
||||||
for(unsigned int i = 0; i < node->mNumChildren; i++)
|
|
||||||
{
|
|
||||||
processNode(node->mChildren[i], scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Mesh processMesh(aiMesh *mesh, const aiScene *scene)
|
|
||||||
{
|
|
||||||
// data to fill
|
|
||||||
vector<Vertex> vertices;
|
|
||||||
vector<unsigned int> indices;
|
|
||||||
vector<Texture> textures;
|
|
||||||
|
|
||||||
// walk through each of the mesh's vertices
|
|
||||||
for(unsigned int 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
|
|
||||||
if (mesh->HasNormals())
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
vertex.TexCoords = glm::vec2(0.0f, 0.0f);
|
|
||||||
|
|
||||||
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(unsigned int 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(unsigned int j = 0; j < face.mNumIndices; j++)
|
|
||||||
indices.push_back(face.mIndices[j]);
|
|
||||||
}
|
|
||||||
// process materials
|
|
||||||
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 = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse", true);
|
|
||||||
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
|
|
||||||
// 2. specular maps
|
|
||||||
vector<Texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
|
|
||||||
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
|
|
||||||
// 3. normal maps
|
|
||||||
std::vector<Texture> normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal");
|
|
||||||
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
|
|
||||||
// 4. height maps
|
|
||||||
std::vector<Texture> heightMaps = 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, bool fallback = false)
|
|
||||||
{
|
|
||||||
vector<Texture> textures;
|
|
||||||
for(unsigned int 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
|
|
||||||
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; // 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.C_Str();
|
|
||||||
textures.push_back(texture);
|
|
||||||
textures_loaded.push_back(texture); // store it as texture loaded for entire model, to ensure we won't unnecessary load duplicate textures.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load DefaultTexture if there isn't one already
|
|
||||||
if (mat->GetTextureCount(type) == 0) {
|
|
||||||
string defaultPath = "DefaultTexture.jpg";
|
|
||||||
|
|
||||||
Texture texture;
|
|
||||||
texture.id = TextureFromFile(defaultPath.c_str(), "assets/");
|
|
||||||
texture.type = typeName;
|
|
||||||
texture.path = defaultPath;
|
|
||||||
textures.push_back(texture);
|
|
||||||
textures_loaded.push_back(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
return textures;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
unsigned inline int TextureFromFile(const char *path, const 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;
|
|
||||||
}
|
|
||||||
#endif //ENGINE2026_MODEL_H
|
|
||||||
186
engine/shader.h
186
engine/shader.h
|
|
@ -1,186 +0,0 @@
|
||||||
#ifndef SHADER_H
|
|
||||||
#define SHADER_H
|
|
||||||
|
|
||||||
#include <glad/glad.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
class Shader
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Compiled Shader Program ID
|
|
||||||
unsigned int ID;
|
|
||||||
|
|
||||||
Shader(const char* vertexPath, const char* fragmentPath)
|
|
||||||
{
|
|
||||||
std::string vertexCode, fragmentCode;
|
|
||||||
std::ifstream vertexFile, fragmentFile;
|
|
||||||
|
|
||||||
vertexFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
|
|
||||||
fragmentFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Open the files
|
|
||||||
vertexFile.open(vertexPath);
|
|
||||||
fragmentFile.open(fragmentPath);
|
|
||||||
std::stringstream vertexStream, fragmentStream;
|
|
||||||
|
|
||||||
// Save the contents to our streams
|
|
||||||
vertexStream << vertexFile.rdbuf();
|
|
||||||
fragmentStream << fragmentFile.rdbuf();
|
|
||||||
|
|
||||||
// Close files
|
|
||||||
vertexFile.close();
|
|
||||||
fragmentFile.close();
|
|
||||||
|
|
||||||
// Save the contents to our strings
|
|
||||||
vertexCode = vertexStream.str();
|
|
||||||
fragmentCode = fragmentStream.str();
|
|
||||||
|
|
||||||
// Fix encoding issues, specifically for Linux
|
|
||||||
//vertexCode.erase(std::remove(vertexCode.begin(), vertexCode.end(), '\r'), vertexCode.end());
|
|
||||||
//fragmentCode.erase(std::remove(fragmentCode.begin(), fragmentCode.end(), '\r'), fragmentCode.end());
|
|
||||||
}
|
|
||||||
catch (std::ifstream::failure& e)
|
|
||||||
{
|
|
||||||
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* vertexShaderSource = vertexCode.c_str();
|
|
||||||
const char* fragmentShaderSource = fragmentCode.c_str();
|
|
||||||
|
|
||||||
unsigned int vertexShader;
|
|
||||||
vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
|
||||||
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
|
|
||||||
glCompileShader(vertexShader);
|
|
||||||
checkCompileErrors(vertexShader, "VERTEX");
|
|
||||||
|
|
||||||
unsigned int fragmentShader;
|
|
||||||
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
||||||
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
|
|
||||||
glCompileShader(fragmentShader);
|
|
||||||
checkCompileErrors(fragmentShader, "FRAGMENT");
|
|
||||||
|
|
||||||
ID = glCreateProgram();
|
|
||||||
glAttachShader(ID, vertexShader);
|
|
||||||
glAttachShader(ID, fragmentShader);
|
|
||||||
glLinkProgram(ID);
|
|
||||||
checkCompileErrors(ID, "PROGRAM");
|
|
||||||
|
|
||||||
glDeleteShader(vertexShader);
|
|
||||||
glDeleteShader(fragmentShader);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Use()
|
|
||||||
{
|
|
||||||
glUseProgram(ID);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Setters
|
|
||||||
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 setArr(const std::string &name, const GLint value[], const int &count) const
|
|
||||||
{
|
|
||||||
glUniform1iv(glGetUniformLocation(ID, name.c_str()), count, value);
|
|
||||||
}
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setDecal(int index, GLint tex, float opacity, const glm::vec4& uvCoords) const {
|
|
||||||
std::string indexStr = std::to_string(index);
|
|
||||||
|
|
||||||
GLint texLoc = glGetUniformLocation(ID, ("decals[" + indexStr + "].tex").c_str());
|
|
||||||
if (texLoc != -1) glUniform1i(texLoc, tex);
|
|
||||||
|
|
||||||
GLint opLoc = glGetUniformLocation(ID, ("decals[" + indexStr + "].opacity").c_str());
|
|
||||||
if (opLoc != -1) glUniform1f(opLoc, opacity);
|
|
||||||
|
|
||||||
GLint uvLoc = glGetUniformLocation(ID, ("decals[" + indexStr + "].uvCoords").c_str());
|
|
||||||
if (uvLoc != -1) {
|
|
||||||
glUniform4fv(uvLoc, 1, &uvCoords[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
void checkCompileErrors(unsigned int shader, std::string type)
|
|
||||||
{
|
|
||||||
int success;
|
|
||||||
char 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <glad/glad.h>
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
#include <stb/stb_image.h>
|
|
||||||
|
|
||||||
class Texture
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
unsigned int ID;
|
|
||||||
int width, height, nrChannels;
|
|
||||||
|
|
||||||
Texture(const char* texturePath, bool flip = false)
|
|
||||||
{
|
|
||||||
// Create the Texture and set our ID
|
|
||||||
glGenTextures(1, &ID);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, ID);
|
|
||||||
|
|
||||||
// set the texture wrapping/filtering options (on the currently bound texture object)
|
|
||||||
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_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
|
|
||||||
// Grab the texture from our path
|
|
||||||
if (flip)
|
|
||||||
stbi_set_flip_vertically_on_load(true);
|
|
||||||
|
|
||||||
unsigned char *data = stbi_load(texturePath, &width, &height, &nrChannels, 0);
|
|
||||||
if (data)
|
|
||||||
{
|
|
||||||
GLenum format = (nrChannels == 4) ? GL_RGBA : GL_RGB;
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
|
|
||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::cout << "Failed to load texture" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flip)
|
|
||||||
stbi_set_flip_vertically_on_load(false);
|
|
||||||
|
|
||||||
stbi_image_free(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Draw() const
|
|
||||||
{
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, ID);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,23 +1,54 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <engine/Backend/Shader.h>
|
||||||
|
#include <engine/Backend/Camera.h>
|
||||||
|
#include <engine/Backend/Model.h>
|
||||||
|
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
namespace game
|
namespace game
|
||||||
{
|
{
|
||||||
|
const int SCREEN_WIDTH = 1366;
|
||||||
|
const int SCREEN_HEIGHT = 768;
|
||||||
|
|
||||||
|
std::optional<Shader> shader;
|
||||||
|
std::optional<Model> model;
|
||||||
|
std::optional<Camera> camera;
|
||||||
|
|
||||||
|
float lastX = SCREEN_WIDTH / 2.0f;
|
||||||
|
float lastY = SCREEN_HEIGHT / 2.0f;
|
||||||
|
bool firstMouse = true;
|
||||||
|
|
||||||
bool Init()
|
bool Init()
|
||||||
{
|
{
|
||||||
|
shader.emplace("assets/shaders/basicVertex.vert", "assets/shaders/basicFragment.frag");
|
||||||
|
shader->Use();
|
||||||
|
|
||||||
|
camera.emplace(glm::vec3(0.0f, 0.0f, 0.0f));
|
||||||
|
model.emplace("assets/models/sponza/Sponza.gltf");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreFrame()
|
void PreFrame()
|
||||||
{
|
{
|
||||||
|
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
shader->Use();
|
||||||
|
|
||||||
|
camera->Update(shader.value(), SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Render()
|
void Render()
|
||||||
{
|
{
|
||||||
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
glm::mat4 modelTransform = glm::mat4(1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
modelTransform = glm::translate(modelTransform, glm::vec3(0.0f, 0.0f, 0.0f));
|
||||||
|
modelTransform = glm::scale(modelTransform, glm::vec3(0.02f, 0.02f, 0.02f));
|
||||||
|
shader->setMat4("model", modelTransform);
|
||||||
|
model->Draw(shader.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
|
|
@ -34,4 +65,5 @@ namespace game
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue