mirror of
https://github.com/JoeyDeVries/LearnOpenGL.git
synced 2026-01-30 20:13:22 +08:00
Initial commit, all code samples with working CMake script for VS/Windows.
This commit is contained in:
133
includes/learnopengl/camera.h
Normal file
133
includes/learnopengl/camera.h
Normal file
@@ -0,0 +1,133 @@
|
||||
#pragma once
|
||||
|
||||
// Std. Includes
|
||||
#include <vector>
|
||||
|
||||
// GL Includes
|
||||
#include <GL\glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
|
||||
|
||||
// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
|
||||
enum Camera_Movement {
|
||||
FORWARD,
|
||||
BACKWARD,
|
||||
LEFT,
|
||||
RIGHT
|
||||
};
|
||||
|
||||
// Default camera values
|
||||
const GLfloat YAW = -90.0f;
|
||||
const GLfloat PITCH = 0.0f;
|
||||
const GLfloat SPEED = 3.0f;
|
||||
const GLfloat SENSITIVTY = 0.25f;
|
||||
const GLfloat ZOOM = 45.0f;
|
||||
|
||||
|
||||
// An abstract camera class that processes input and calculates the corresponding Eular Angles, Vectors and Matrices for use in OpenGL
|
||||
class Camera
|
||||
{
|
||||
public:
|
||||
// Camera Attributes
|
||||
glm::vec3 Position;
|
||||
glm::vec3 Front;
|
||||
glm::vec3 Up;
|
||||
glm::vec3 Right;
|
||||
glm::vec3 WorldUp;
|
||||
// Eular Angles
|
||||
GLfloat Yaw;
|
||||
GLfloat Pitch;
|
||||
// Camera options
|
||||
GLfloat MovementSpeed;
|
||||
GLfloat MouseSensitivity;
|
||||
GLfloat Zoom;
|
||||
|
||||
// Constructor with vectors
|
||||
Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), GLfloat yaw = YAW, GLfloat pitch = PITCH) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVTY), Zoom(ZOOM)
|
||||
{
|
||||
this->Position = position;
|
||||
this->WorldUp = up;
|
||||
this->Yaw = yaw;
|
||||
this->Pitch = pitch;
|
||||
this->updateCameraVectors();
|
||||
}
|
||||
// Constructor with scalar values
|
||||
Camera(GLfloat posX, GLfloat posY, GLfloat posZ, GLfloat upX, GLfloat upY, GLfloat upZ, GLfloat yaw, GLfloat pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVTY), Zoom(ZOOM)
|
||||
{
|
||||
this->Position = glm::vec3(posX, posY, posZ);
|
||||
this->WorldUp = glm::vec3(upX, upY, upZ);
|
||||
this->Yaw = yaw;
|
||||
this->Pitch = pitch;
|
||||
this->updateCameraVectors();
|
||||
}
|
||||
|
||||
// Returns the view matrix calculated using Eular Angles and the LookAt Matrix
|
||||
glm::mat4 GetViewMatrix()
|
||||
{
|
||||
return glm::lookAt(this->Position, this->Position + this->Front, this->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, GLfloat deltaTime)
|
||||
{
|
||||
GLfloat velocity = this->MovementSpeed * deltaTime;
|
||||
if (direction == FORWARD)
|
||||
this->Position += this->Front * velocity;
|
||||
if (direction == BACKWARD)
|
||||
this->Position -= this->Front * velocity;
|
||||
if (direction == LEFT)
|
||||
this->Position -= this->Right * velocity;
|
||||
if (direction == RIGHT)
|
||||
this->Position += this->Right * velocity;
|
||||
}
|
||||
|
||||
// Processes input received from a mouse input system. Expects the offset value in both the x and y direction.
|
||||
void ProcessMouseMovement(GLfloat xoffset, GLfloat yoffset, GLboolean constrainPitch = true)
|
||||
{
|
||||
xoffset *= this->MouseSensitivity;
|
||||
yoffset *= this->MouseSensitivity;
|
||||
|
||||
this->Yaw += xoffset;
|
||||
this->Pitch += yoffset;
|
||||
|
||||
// Make sure that when pitch is out of bounds, screen doesn't get flipped
|
||||
if (constrainPitch)
|
||||
{
|
||||
if (this->Pitch > 89.0f)
|
||||
this->Pitch = 89.0f;
|
||||
if (this->Pitch < -89.0f)
|
||||
this->Pitch = -89.0f;
|
||||
}
|
||||
|
||||
// Update Front, Right and Up Vectors using the updated Eular angles
|
||||
this->updateCameraVectors();
|
||||
}
|
||||
|
||||
// Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
|
||||
void ProcessMouseScroll(GLfloat yoffset)
|
||||
{
|
||||
if (this->Zoom >= 1.0f && this->Zoom <= 45.0f)
|
||||
this->Zoom -= yoffset;
|
||||
if (this->Zoom <= 1.0f)
|
||||
this->Zoom = 1.0f;
|
||||
if (this->Zoom >= 45.0f)
|
||||
this->Zoom = 45.0f;
|
||||
}
|
||||
|
||||
private:
|
||||
// Calculates the front vector from the Camera's (updated) Eular Angles
|
||||
void updateCameraVectors()
|
||||
{
|
||||
// Calculate the new Front vector
|
||||
glm::vec3 front;
|
||||
front.x = cos(glm::radians(this->Yaw)) * cos(glm::radians(this->Pitch));
|
||||
front.y = sin(glm::radians(this->Pitch));
|
||||
front.z = sin(glm::radians(this->Yaw)) * cos(glm::radians(this->Pitch));
|
||||
this->Front = glm::normalize(front);
|
||||
// Also re-calculate the Right and Up vector
|
||||
this->Right = glm::normalize(glm::cross(this->Front, this->WorldUp)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
|
||||
this->Up = glm::normalize(glm::cross(this->Right, this->Front));
|
||||
}
|
||||
};
|
||||
127
includes/learnopengl/mesh.h
Normal file
127
includes/learnopengl/mesh.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
// Std. Includes
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
// GL Includes
|
||||
#include <GL/glew.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;
|
||||
};
|
||||
|
||||
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;
|
||||
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
|
||||
number = ss.str();
|
||||
// Now set the sampler to the correct texture unit
|
||||
glUniform1f(glGetUniformLocation(shader.Program, (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), (GLvoid*)0);
|
||||
// Vertex Normals
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, Normal));
|
||||
// Vertex Texture Coords
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, TexCoords));
|
||||
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
211
includes/learnopengl/model.h
Normal file
211
includes/learnopengl/model.h
Normal file
@@ -0,0 +1,211 @@
|
||||
#pragma once
|
||||
// Std. Includes
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
// GL Includes
|
||||
#include <GL/glew.h> // Contains all the necessery OpenGL includes
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <SOIL.h>
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/postprocess.h>
|
||||
|
||||
#include "Mesh.h"
|
||||
|
||||
GLint TextureFromFile(const char* path, string directory);
|
||||
|
||||
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;
|
||||
|
||||
/* Functions */
|
||||
// Constructor, expects a filepath to a 3D model.
|
||||
Model(GLchar* path)
|
||||
{
|
||||
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);
|
||||
// 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);
|
||||
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());
|
||||
}
|
||||
|
||||
// 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(textures_loaded[j].path == str)
|
||||
{
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
GLint TextureFromFile(const char* path, string directory)
|
||||
{
|
||||
//Generate texture ID and load texture data
|
||||
string filename = string(path);
|
||||
filename = directory + '/' + filename;
|
||||
GLuint textureID;
|
||||
glGenTextures(1, &textureID);
|
||||
int width,height;
|
||||
unsigned char* image = SOIL_load_image(filename.c_str(), &width, &height, 0, SOIL_LOAD_RGB);
|
||||
// Assign texture to ID
|
||||
glBindTexture(GL_TEXTURE_2D, textureID);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
// Parameters
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
SOIL_free_image_data(image);
|
||||
return textureID;
|
||||
}
|
||||
121
includes/learnopengl/shader.h
Normal file
121
includes/learnopengl/shader.h
Normal file
@@ -0,0 +1,121 @@
|
||||
#ifndef SHADER_H
|
||||
#define SHADER_H
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
class Shader
|
||||
{
|
||||
public:
|
||||
GLuint Program;
|
||||
// Constructor generates the shader on the fly
|
||||
Shader(const GLchar* vertexPath, const GLchar* fragmentPath, const GLchar* geometryPath = nullptr)
|
||||
{
|
||||
// 1. Retrieve the vertex/fragment source code from filePath
|
||||
std::string vertexCode;
|
||||
std::string fragmentCode;
|
||||
std::string geometryCode;
|
||||
try
|
||||
{
|
||||
// Open files
|
||||
std::ifstream vShaderFile(vertexPath);
|
||||
std::ifstream fShaderFile(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();
|
||||
// If geometry shader path is present, also load a geometry shader
|
||||
if(geometryPath != nullptr)
|
||||
{
|
||||
std::ifstream gShaderFile(geometryPath);
|
||||
std::stringstream gShaderStream;
|
||||
gShaderStream << gShaderFile.rdbuf();
|
||||
gShaderFile.close();
|
||||
geometryCode = gShaderStream.str();
|
||||
}
|
||||
}
|
||||
catch (std::exception e)
|
||||
{
|
||||
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
|
||||
}
|
||||
const GLchar* vShaderCode = vertexCode.c_str();
|
||||
const GLchar * fShaderCode = fragmentCode.c_str();
|
||||
// 2. Compile shaders
|
||||
GLuint vertex, fragment;
|
||||
GLint success;
|
||||
GLchar 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");
|
||||
// If geometry shader is given, compile geometry shader
|
||||
GLuint geometry;
|
||||
if(geometryPath != nullptr)
|
||||
{
|
||||
const GLchar * gShaderCode = geometryCode.c_str();
|
||||
geometry = glCreateShader(GL_GEOMETRY_SHADER);
|
||||
glShaderSource(geometry, 1, &gShaderCode, NULL);
|
||||
glCompileShader(geometry);
|
||||
checkCompileErrors(geometry, "GEOMETRY");
|
||||
}
|
||||
// Shader Program
|
||||
this->Program = glCreateProgram();
|
||||
glAttachShader(this->Program, vertex);
|
||||
glAttachShader(this->Program, fragment);
|
||||
if(geometryPath != nullptr)
|
||||
glAttachShader(this->Program, geometry);
|
||||
glLinkProgram(this->Program);
|
||||
checkCompileErrors(this->Program, "PROGRAM");
|
||||
// Delete the shaders as they're linked into our program now and no longer necessery
|
||||
glDeleteShader(vertex);
|
||||
glDeleteShader(fragment);
|
||||
if(geometryPath != nullptr)
|
||||
glDeleteShader(geometry);
|
||||
|
||||
}
|
||||
// Uses the current shader
|
||||
void Use() { glUseProgram(this->Program); }
|
||||
|
||||
private:
|
||||
void checkCompileErrors(GLuint shader, std::string type)
|
||||
{
|
||||
GLint success;
|
||||
GLchar infoLog[1024];
|
||||
if(type != "PROGRAM")
|
||||
{
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||
if(!success)
|
||||
{
|
||||
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
|
||||
std::cout << "| ERROR::::SHADER-COMPILATION-ERROR of type: " << type << "|\n" << infoLog << "\n| -- --------------------------------------------------- -- |" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
glGetProgramiv(shader, GL_LINK_STATUS, &success);
|
||||
if(!success)
|
||||
{
|
||||
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
|
||||
std::cout << "| ERROR::::PROGRAM-LINKING-ERROR of type: " << type << "|\n" << infoLog << "\n| -- --------------------------------------------------- -- |" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user