Add basic setup for shadow rendering

adapted from the previous ECS project. working to some extent, but far from ideal
This commit is contained in:
karl 2021-05-20 00:15:58 +02:00
parent eea0c57e7c
commit 3c35a730f5
15 changed files with 1639 additions and 11 deletions

View File

@ -58,3 +58,4 @@ testEnv.Program('test/bin/vector-test.out', [catch_cpp, 'test/vector/vector-test
testEnv.Program('test/bin/test-app.out', Glob('test/test-app/*.cpp'))
testEnv.Program('test/bin/parallax-demo.out', Glob('test/parallax-demo/*.cpp'))
testEnv.Program('test/bin/particle-demo.out', Glob('test/particle-demo/*.cpp'))
testEnv.Program('test/bin/shadow-demo.out', Glob('test/shadow-demo/*.cpp'))

View File

@ -4,8 +4,9 @@ out vec4 FragColor;
in VS_OUT {
vec3 FragPos;
vec4 FragPosLightSpace;
vec2 TexCoords;
vec3 TangentLightPos;
vec3 TangentLightDir;
vec3 TangentViewPos;
vec3 TangentFragPos;
} fs_in;
@ -13,6 +14,7 @@ in VS_OUT {
layout (binding = 0) uniform sampler2D albedoMap;
layout (binding = 1) uniform sampler2D normalMap;
layout (binding = 2) uniform sampler2D depthMap;
layout (binding = 3) uniform sampler2D shadowMap;
uniform float bump_depth;
uniform float number_of_steps;
@ -57,6 +59,28 @@ vec2 get_parallax_offset_uv(vec2 uv, vec3 view_direction) {
return current_uv;
}
float get_shadow(vec4 fragPosLightSpace, vec3 normal) {
// The bias varies depending on the angle to the light (the steeper the angle, the bigger the bias needs to be)
mediump float bias = max(0.005 * (1.0 - dot(normal, fs_in.TangentLightDir)), 0.0005);
// perform perspective divide
mediump vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
// transform to [0,1] range
projCoords = projCoords * 0.5 + 0.5;
// If we're outside of [0.0, 1.0] in the coordinates, return 0
if (projCoords.x < 0.0 || projCoords.y < 0.0 || projCoords.x > 1.0 || projCoords.y > 1.0) return 0.0;
// get closest depth value from light's perspective (using [0,1] range fragPosLight as coords)
mediump float closestDepth = texture(shadowMap, projCoords.xy).r;
// get depth of current fragment from light's perspective
mediump float currentDepth = projCoords.z;
// check whether current frag pos is in shadow
mediump float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0;
return shadow;
}
void main() {
// Offset texture coordinates with Parallax Mapping
vec3 view_direction = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos);
@ -80,7 +104,7 @@ void main() {
vec3 ambient = 0.1 * color;
// Apply albedo with intensity based on the dot product between the light direction and the normal here
vec3 light_direction = normalize(fs_in.TangentLightPos - fs_in.TangentFragPos);
vec3 light_direction = fs_in.TangentLightDir;
float light_normal_dot = max(dot(light_direction, normal), 0.0);
vec3 albedo = light_normal_dot * color;
@ -90,7 +114,9 @@ void main() {
vec3 specular = vec3(0.2) * spec;
float shadow = get_shadow(fs_in.FragPosLightSpace, normal);
// Apply
FragColor = vec4(ambient + albedo + specular, 1.0);
FragColor = vec4(ambient + albedo + specular - shadow * 4.0, 1.0);
}

View File

@ -8,8 +8,9 @@ layout (location = 4) in vec3 aBitangent;
out VS_OUT {
vec3 FragPos;
vec4 FragPosLightSpace;
vec2 TexCoords;
vec3 TangentLightPos;
vec3 TangentLightDir;
vec3 TangentViewPos;
vec3 TangentFragPos;
} vs_out;
@ -18,20 +19,23 @@ uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform vec3 lightPos;
uniform vec3 light_direction;
uniform mat4 light_space_matrix;
uniform vec3 viewPos;
void main() {
gl_Position = projection * view * model * vec4(aPos, 1.0);
vs_out.FragPos = vec3(model * vec4(aPos, 1.0));
vs_out.TexCoords = aTexCoords;
vs_out.TexCoords = aTexCoords;
vs_out.FragPosLightSpace = light_space_matrix * vec4(vs_out.FragPos, 1.0);
vec3 T = normalize(mat3(model) * aTangent);
vec3 B = normalize(mat3(model) * aBitangent);
vec3 N = normalize(mat3(model) * aNormal);
mat3 TBN = transpose(mat3(T, B, N));
vs_out.TangentLightPos = TBN * lightPos;
vs_out.TangentLightDir = light_direction;
vs_out.TangentViewPos = TBN * viewPos;
vs_out.TangentFragPos = TBN * vs_out.FragPos;
}

21
Shader/depth-debug.fs Normal file
View File

@ -0,0 +1,21 @@
#version 430
out vec4 FragColor;
in vec2 TexCoords;
layout (location = 0) uniform sampler2D depthMap;
// required when using a perspective projection matrix
float LinearizeDepth(float depth)
{
float z = depth * 2.0 - 1.0; // Back to NDC
return (2.0 * 0.1 * 100.0) / (100.0 + 0.1 - z * (100.0 - 0.1));
}
void main()
{
float depthValue = texture(depthMap, TexCoords).r;
// FragColor = vec4(vec3(LinearizeDepth(depthValue) / 100.0), 1.0); // perspective
FragColor = vec4(vec3(depthValue) * 10.0, 1.0); // orthographic
}

13
Shader/depth-debug.vs Normal file
View File

@ -0,0 +1,13 @@
#version 430
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
void main()
{
TexCoords = aTexCoords;
gl_Position = vec4(aPos, 1.0);
}

7
Shader/shadow.fs Normal file
View File

@ -0,0 +1,7 @@
#version 430
void main()
{
// This happens implicitly anyways
// gl_FragDepth = gl_FragCoord.z;
}

10
Shader/shadow.vs Normal file
View File

@ -0,0 +1,10 @@
#version 430
layout (location = 0) in vec3 aPos;
uniform mat4 lightSpaceMatrix;
uniform mat4 model;
void main()
{
gl_Position = lightSpaceMatrix * model * vec4(aPos, 1.0);
}

View File

@ -17,7 +17,7 @@ void RenderBackend::initialize_window(unsigned int width, unsigned int height, S
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
// FIXME: Disabled because of a bug with particles: they're discarded as if the floor moves with the camera
// glEnable(GL_DEPTH_TEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

977
cpp/vendor/OBJ_Loader.h vendored Normal file
View File

@ -0,0 +1,977 @@
// OBJ_Loader.h - A Single Header OBJ Model Loader
#pragma once
// Iostream - STD I/O Library
#include <iostream>
// Vector - STD Vector/Array Library
#include <vector>
// String - STD String Library
#include <string>
// fStream - STD File I/O Library
#include <fstream>
// Math.h - STD math Library
#include <math.h>
// Print progress to console while loading (large models)
#define OBJL_CONSOLE_OUTPUT
// Namespace: OBJL
//
// Description: The namespace that holds eveyrthing that
// is needed and used for the OBJ Model Loader
namespace objl {
// Structure: Vector2
//
// Description: A 2D Vector that Holds Positional Data
struct Vector2 {
// Default Constructor
Vector2() {
X = 0.0f;
Y = 0.0f;
}
// Variable Set Constructor
Vector2(float X_, float Y_) {
X = X_;
Y = Y_;
}
// Bool Equals Operator Overload
bool operator==(const Vector2 &other) const {
return (this->X == other.X && this->Y == other.Y);
}
// Bool Not Equals Operator Overload
bool operator!=(const Vector2 &other) const {
return !(this->X == other.X && this->Y == other.Y);
}
// Addition Operator Overload
Vector2 operator+(const Vector2 &right) const {
return Vector2(this->X + right.X, this->Y + right.Y);
}
// Subtraction Operator Overload
Vector2 operator-(const Vector2 &right) const {
return Vector2(this->X - right.X, this->Y - right.Y);
}
// Float Multiplication Operator Overload
Vector2 operator*(const float &other) const {
return Vector2(this->X * other, this->Y * other);
}
// Positional Variables
float X;
float Y;
};
// Structure: Vector3
//
// Description: A 3D Vector that Holds Positional Data
struct Vector3 {
// Default Constructor
Vector3() {
X = 0.0f;
Y = 0.0f;
Z = 0.0f;
}
// Variable Set Constructor
Vector3(float X_, float Y_, float Z_) {
X = X_;
Y = Y_;
Z = Z_;
}
// Bool Equals Operator Overload
bool operator==(const Vector3 &other) const {
return (this->X == other.X && this->Y == other.Y && this->Z == other.Z);
}
// Bool Not Equals Operator Overload
bool operator!=(const Vector3 &other) const {
return !(this->X == other.X && this->Y == other.Y && this->Z == other.Z);
}
// Addition Operator Overload
Vector3 operator+(const Vector3 &right) const {
return Vector3(this->X + right.X, this->Y + right.Y, this->Z + right.Z);
}
// Subtraction Operator Overload
Vector3 operator-(const Vector3 &right) const {
return Vector3(this->X - right.X, this->Y - right.Y, this->Z - right.Z);
}
// Float Multiplication Operator Overload
Vector3 operator*(const float &other) const {
return Vector3(this->X * other, this->Y * other, this->Z * other);
}
// Float Division Operator Overload
Vector3 operator/(const float &other) const {
return Vector3(this->X / other, this->Y / other, this->Z / other);
}
// Positional Variables
float X;
float Y;
float Z;
};
// Structure: Vertex
//
// Description: Model Vertex object that holds
// a Position, Normal, and Texture Coordinate
struct Vertex {
// Position Vector
Vector3 Position;
// Normal Vector
Vector3 Normal;
// Texture Coordinate Vector
Vector2 TextureCoordinate;
};
struct Material {
Material() {
name;
Ns = 0.0f;
Ni = 0.0f;
d = 0.0f;
illum = 0;
}
// Material Name
std::string name;
// Ambient Color
Vector3 Ka;
// Diffuse Color
Vector3 Kd;
// Specular Color
Vector3 Ks;
// Specular Exponent
float Ns;
// Optical Density
float Ni;
// Dissolve
float d;
// Illumination
int illum;
// Ambient Texture Map
std::string map_Ka;
// Diffuse Texture Map
std::string map_Kd;
// Specular Texture Map
std::string map_Ks;
// Specular Hightlight Map
std::string map_Ns;
// Alpha Texture Map
std::string map_d;
// Bump Map
std::string map_bump;
};
// Structure: Mesh
//
// Description: A Simple Mesh Object that holds
// a name, a vertex list, and an index list
struct Mesh {
// Default Constructor
Mesh() {}
// Variable Set Constructor
Mesh(std::vector<Vertex> &_Vertices, std::vector<unsigned int> &_Indices) {
Vertices = _Vertices;
Indices = _Indices;
}
// Mesh Name
std::string MeshName;
// Vertex List
std::vector<Vertex> Vertices;
// Index List
std::vector<unsigned int> Indices;
// Material
Material MeshMaterial;
};
// Namespace: Math
//
// Description: The namespace that holds all of the math
// functions need for OBJL
namespace math {
// Vector3 Cross Product
Vector3 CrossV3(const Vector3 a, const Vector3 b) {
return Vector3(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X);
}
// Vector3 Magnitude Calculation
float MagnitudeV3(const Vector3 in) {
return (sqrtf(powf(in.X, 2) + powf(in.Y, 2) + powf(in.Z, 2)));
}
// Vector3 DotProduct
float DotV3(const Vector3 a, const Vector3 b) {
return (a.X * b.X) + (a.Y * b.Y) + (a.Z * b.Z);
}
// Angle between 2 Vector3 Objects
float AngleBetweenV3(const Vector3 a, const Vector3 b) {
float angle = DotV3(a, b);
angle /= (MagnitudeV3(a) * MagnitudeV3(b));
return angle = acosf(angle);
}
// Projection Calculation of a onto b
Vector3 ProjV3(const Vector3 a, const Vector3 b) {
Vector3 bn = b / MagnitudeV3(b);
return bn * DotV3(a, bn);
}
} // namespace math
// Namespace: Algorithm
//
// Description: The namespace that holds all of the
// Algorithms needed for OBJL
namespace algorithm {
// Vector3 Multiplication Opertor Overload
Vector3 operator*(const float &left, const Vector3 &right) {
return Vector3(right.X * left, right.Y * left, right.Z * left);
}
// A test to see if P1 is on the same side as P2 of a line segment ab
bool SameSide(Vector3 p1, Vector3 p2, Vector3 a, Vector3 b) {
Vector3 cp1 = math::CrossV3(b - a, p1 - a);
Vector3 cp2 = math::CrossV3(b - a, p2 - a);
if (math::DotV3(cp1, cp2) >= 0)
return true;
else
return false;
}
// Generate a cross produect normal for a triangle
Vector3 GenTriNormal(Vector3 t1, Vector3 t2, Vector3 t3) {
Vector3 u = t2 - t1;
Vector3 v = t3 - t1;
Vector3 normal = math::CrossV3(u, v);
return normal;
}
// Check to see if a Vector3 Point is within a 3 Vector3 Triangle
bool inTriangle(Vector3 point, Vector3 tri1, Vector3 tri2, Vector3 tri3) {
// Test to see if it is within an infinite prism that the triangle outlines.
bool within_tri_prisim = SameSide(point, tri1, tri2, tri3) &&
SameSide(point, tri2, tri1, tri3) && SameSide(point, tri3, tri1, tri2);
// If it isn't it will never be on the triangle
if (!within_tri_prisim) return false;
// Calulate Triangle's Normal
Vector3 n = GenTriNormal(tri1, tri2, tri3);
// Project the point onto this normal
Vector3 proj = math::ProjV3(point, n);
// If the distance from the triangle to the point is 0
// it lies on the triangle
if (math::MagnitudeV3(proj) == 0)
return true;
else
return false;
}
// Split a String into a string array at a given token
inline void split(const std::string &in, std::vector<std::string> &out, std::string token) {
out.clear();
std::string temp;
for (int i = 0; i < int(in.size()); i++) {
std::string test = in.substr(i, token.size());
if (test == token) {
if (!temp.empty()) {
out.push_back(temp);
temp.clear();
i += (int)token.size() - 1;
} else {
out.push_back("");
}
} else if (i + token.size() >= in.size()) {
temp += in.substr(i, token.size());
out.push_back(temp);
break;
} else {
temp += in[i];
}
}
}
// Get tail of string after first token and possibly following spaces
inline std::string tail(const std::string &in) {
size_t token_start = in.find_first_not_of(" \t");
size_t space_start = in.find_first_of(" \t", token_start);
size_t tail_start = in.find_first_not_of(" \t", space_start);
size_t tail_end = in.find_last_not_of(" \t");
if (tail_start != std::string::npos && tail_end != std::string::npos) {
return in.substr(tail_start, tail_end - tail_start + 1);
} else if (tail_start != std::string::npos) {
return in.substr(tail_start);
}
return "";
}
// Get first token of string
inline std::string firstToken(const std::string &in) {
if (!in.empty()) {
size_t token_start = in.find_first_not_of(" \t");
size_t token_end = in.find_first_of(" \t", token_start);
if (token_start != std::string::npos && token_end != std::string::npos) {
return in.substr(token_start, token_end - token_start);
} else if (token_start != std::string::npos) {
return in.substr(token_start);
}
}
return "";
}
// Get element at given index position
template <class T> inline const T &getElement(const std::vector<T> &elements, std::string &index) {
int idx = std::stoi(index);
if (idx < 0)
idx = int(elements.size()) + idx;
else
idx--;
return elements[idx];
}
} // namespace algorithm
// Class: Loader
//
// Description: The OBJ Model Loader
class Loader {
public:
// Default Constructor
Loader() {}
~Loader() { LoadedMeshes.clear(); }
// Load a file into the loader
//
// If file is loaded return true
//
// If the file is unable to be found
// or unable to be loaded return false
bool LoadFile(std::string Path) {
// If the file is not an .obj file return false
if (Path.substr(Path.size() - 4, 4) != ".obj") return false;
std::ifstream file(Path);
if (!file.is_open()) return false;
LoadedMeshes.clear();
LoadedVertices.clear();
LoadedIndices.clear();
std::vector<Vector3> Positions;
std::vector<Vector2> TCoords;
std::vector<Vector3> Normals;
std::vector<Vertex> Vertices;
std::vector<unsigned int> Indices;
std::vector<std::string> MeshMatNames;
bool listening = false;
std::string meshname;
Mesh tempMesh;
#ifdef OBJL_CONSOLE_OUTPUT
const unsigned int outputEveryNth = 1000;
unsigned int outputIndicator = outputEveryNth;
#endif
std::string curline;
while (std::getline(file, curline)) {
#ifdef OBJL_CONSOLE_OUTPUT
if ((outputIndicator = ((outputIndicator + 1) % outputEveryNth)) == 1) {
if (!meshname.empty()) {
std::cout << "\r- " << meshname << "\t| vertices > " << Positions.size()
<< "\t| texcoords > " << TCoords.size() << "\t| normals > "
<< Normals.size() << "\t| triangles > " << (Vertices.size() / 3)
<< (!MeshMatNames.empty() ? "\t| material: " + MeshMatNames.back()
: "");
}
}
#endif
// Generate a Mesh Object or Prepare for an object to be created
if (algorithm::firstToken(curline) == "o" || algorithm::firstToken(curline) == "g" ||
curline[0] == 'g') {
if (!listening) {
listening = true;
if (algorithm::firstToken(curline) == "o" ||
algorithm::firstToken(curline) == "g") {
meshname = algorithm::tail(curline);
} else {
meshname = "unnamed";
}
} else {
// Generate the mesh to put into the array
if (!Indices.empty() && !Vertices.empty()) {
// Create Mesh
tempMesh = Mesh(Vertices, Indices);
tempMesh.MeshName = meshname;
// Insert Mesh
LoadedMeshes.push_back(tempMesh);
// Cleanup
Vertices.clear();
Indices.clear();
meshname.clear();
meshname = algorithm::tail(curline);
} else {
if (algorithm::firstToken(curline) == "o" ||
algorithm::firstToken(curline) == "g") {
meshname = algorithm::tail(curline);
} else {
meshname = "unnamed";
}
}
}
#ifdef OBJL_CONSOLE_OUTPUT
std::cout << std::endl;
outputIndicator = 0;
#endif
}
// Generate a Vertex Position
if (algorithm::firstToken(curline) == "v") {
std::vector<std::string> spos;
Vector3 vpos;
algorithm::split(algorithm::tail(curline), spos, " ");
vpos.X = std::stof(spos[0]);
vpos.Y = std::stof(spos[1]);
vpos.Z = std::stof(spos[2]);
Positions.push_back(vpos);
}
// Generate a Vertex Texture Coordinate
if (algorithm::firstToken(curline) == "vt") {
std::vector<std::string> stex;
Vector2 vtex;
algorithm::split(algorithm::tail(curline), stex, " ");
vtex.X = std::stof(stex[0]);
vtex.Y = std::stof(stex[1]);
TCoords.push_back(vtex);
}
// Generate a Vertex Normal;
if (algorithm::firstToken(curline) == "vn") {
std::vector<std::string> snor;
Vector3 vnor;
algorithm::split(algorithm::tail(curline), snor, " ");
vnor.X = std::stof(snor[0]);
vnor.Y = std::stof(snor[1]);
vnor.Z = std::stof(snor[2]);
Normals.push_back(vnor);
}
// Generate a Face (vertices & indices)
if (algorithm::firstToken(curline) == "f") {
// Generate the vertices
std::vector<Vertex> vVerts;
GenVerticesFromRawOBJ(vVerts, Positions, TCoords, Normals, curline);
// Add Vertices
for (int i = 0; i < int(vVerts.size()); i++) {
Vertices.push_back(vVerts[i]);
LoadedVertices.push_back(vVerts[i]);
}
std::vector<unsigned int> iIndices;
VertexTriangluation(iIndices, vVerts);
// Add Indices
for (int i = 0; i < int(iIndices.size()); i++) {
unsigned int indnum =
(unsigned int)((Vertices.size()) - vVerts.size()) + iIndices[i];
Indices.push_back(indnum);
indnum = (unsigned int)((LoadedVertices.size()) - vVerts.size()) + iIndices[i];
LoadedIndices.push_back(indnum);
}
}
// Get Mesh Material Name
if (algorithm::firstToken(curline) == "usemtl") {
MeshMatNames.push_back(algorithm::tail(curline));
// Create new Mesh, if Material changes within a group
if (!Indices.empty() && !Vertices.empty()) {
// Create Mesh
tempMesh = Mesh(Vertices, Indices);
tempMesh.MeshName = meshname;
int i = 2;
while (1) {
tempMesh.MeshName = meshname + "_" + std::to_string(i);
for (auto &m : LoadedMeshes)
if (m.MeshName == tempMesh.MeshName) continue;
break;
}
// Insert Mesh
LoadedMeshes.push_back(tempMesh);
// Cleanup
Vertices.clear();
Indices.clear();
}
#ifdef OBJL_CONSOLE_OUTPUT
outputIndicator = 0;
#endif
}
// Load Materials
if (algorithm::firstToken(curline) == "mtllib") {
// Generate LoadedMaterial
// Generate a path to the material file
std::vector<std::string> temp;
algorithm::split(Path, temp, "/");
std::string pathtomat = "";
if (temp.size() != 1) {
for (int i = 0; i < temp.size() - 1; i++) {
pathtomat += temp[i] + "/";
}
}
pathtomat += algorithm::tail(curline);
#ifdef OBJL_CONSOLE_OUTPUT
std::cout << std::endl << "- find materials in: " << pathtomat << std::endl;
#endif
// Load Materials
LoadMaterials(pathtomat);
}
}
#ifdef OBJL_CONSOLE_OUTPUT
std::cout << std::endl;
#endif
// Deal with last mesh
if (!Indices.empty() && !Vertices.empty()) {
// Create Mesh
tempMesh = Mesh(Vertices, Indices);
tempMesh.MeshName = meshname;
// Insert Mesh
LoadedMeshes.push_back(tempMesh);
}
file.close();
// Set Materials for each Mesh
for (int i = 0; i < MeshMatNames.size(); i++) {
std::string matname = MeshMatNames[i];
// Find corresponding material name in loaded materials
// when found copy material variables into mesh material
for (int j = 0; j < LoadedMaterials.size(); j++) {
if (LoadedMaterials[j].name == matname) {
LoadedMeshes[i].MeshMaterial = LoadedMaterials[j];
break;
}
}
}
if (LoadedMeshes.empty() && LoadedVertices.empty() && LoadedIndices.empty()) {
return false;
} else {
return true;
}
}
// Loaded Mesh Objects
std::vector<Mesh> LoadedMeshes;
// Loaded Vertex Objects
std::vector<Vertex> LoadedVertices;
// Loaded Index Positions
std::vector<unsigned int> LoadedIndices;
// Loaded Material Objects
std::vector<Material> LoadedMaterials;
private:
// Generate vertices from a list of positions,
// tcoords, normals and a face line
void GenVerticesFromRawOBJ(std::vector<Vertex> &oVerts, const std::vector<Vector3> &iPositions,
const std::vector<Vector2> &iTCoords,
const std::vector<Vector3> &iNormals, std::string icurline) {
std::vector<std::string> sface, svert;
Vertex vVert;
algorithm::split(algorithm::tail(icurline), sface, " ");
bool noNormal = false;
// For every given vertex do this
for (int i = 0; i < int(sface.size()); i++) {
// See What type the vertex is.
int vtype;
algorithm::split(sface[i], svert, "/");
// Check for just position - v1
if (svert.size() == 1) {
// Only position
vtype = 1;
}
// Check for position & texture - v1/vt1
if (svert.size() == 2) {
// Position & Texture
vtype = 2;
}
// Check for Position, Texture and Normal - v1/vt1/vn1
// or if Position and Normal - v1//vn1
if (svert.size() == 3) {
if (svert[1] != "") {
// Position, Texture, and Normal
vtype = 4;
} else {
// Position & Normal
vtype = 3;
}
}
// Calculate and store the vertex
switch (vtype) {
case 1: // P
{
vVert.Position = algorithm::getElement(iPositions, svert[0]);
vVert.TextureCoordinate = Vector2(0, 0);
noNormal = true;
oVerts.push_back(vVert);
break;
}
case 2: // P/T
{
vVert.Position = algorithm::getElement(iPositions, svert[0]);
vVert.TextureCoordinate = algorithm::getElement(iTCoords, svert[1]);
noNormal = true;
oVerts.push_back(vVert);
break;
}
case 3: // P//N
{
vVert.Position = algorithm::getElement(iPositions, svert[0]);
vVert.TextureCoordinate = Vector2(0, 0);
vVert.Normal = algorithm::getElement(iNormals, svert[2]);
oVerts.push_back(vVert);
break;
}
case 4: // P/T/N
{
vVert.Position = algorithm::getElement(iPositions, svert[0]);
vVert.TextureCoordinate = algorithm::getElement(iTCoords, svert[1]);
vVert.Normal = algorithm::getElement(iNormals, svert[2]);
oVerts.push_back(vVert);
break;
}
default: {
break;
}
}
}
// take care of missing normals
// these may not be truly acurate but it is the
// best they get for not compiling a mesh with normals
if (noNormal) {
Vector3 A = oVerts[0].Position - oVerts[1].Position;
Vector3 B = oVerts[2].Position - oVerts[1].Position;
Vector3 normal = math::CrossV3(A, B);
for (int i = 0; i < int(oVerts.size()); i++) {
oVerts[i].Normal = normal;
}
}
}
// Triangulate a list of vertices into a face by printing
// inducies corresponding with triangles within it
void VertexTriangluation(std::vector<unsigned int> &oIndices,
const std::vector<Vertex> &iVerts) {
// If there are 2 or less verts,
// no triangle can be created,
// so exit
if (iVerts.size() < 3) { return; }
// If it is a triangle no need to calculate it
if (iVerts.size() == 3) {
oIndices.push_back(0);
oIndices.push_back(1);
oIndices.push_back(2);
return;
}
// Create a list of vertices
std::vector<Vertex> tVerts = iVerts;
while (true) {
// For every vertex
for (int i = 0; i < int(tVerts.size()); i++) {
// pPrev = the previous vertex in the list
Vertex pPrev;
if (i == 0) {
pPrev = tVerts[tVerts.size() - 1];
} else {
pPrev = tVerts[i - 1];
}
// pCur = the current vertex;
Vertex pCur = tVerts[i];
// pNext = the next vertex in the list
Vertex pNext;
if (i == tVerts.size() - 1) {
pNext = tVerts[0];
} else {
pNext = tVerts[i + 1];
}
// Check to see if there are only 3 verts left
// if so this is the last triangle
if (tVerts.size() == 3) {
// Create a triangle from pCur, pPrev, pNext
for (int j = 0; j < int(tVerts.size()); j++) {
if (iVerts[j].Position == pCur.Position) oIndices.push_back(j);
if (iVerts[j].Position == pPrev.Position) oIndices.push_back(j);
if (iVerts[j].Position == pNext.Position) oIndices.push_back(j);
}
tVerts.clear();
break;
}
if (tVerts.size() == 4) {
// Create a triangle from pCur, pPrev, pNext
for (int j = 0; j < int(iVerts.size()); j++) {
if (iVerts[j].Position == pCur.Position) oIndices.push_back(j);
if (iVerts[j].Position == pPrev.Position) oIndices.push_back(j);
if (iVerts[j].Position == pNext.Position) oIndices.push_back(j);
}
Vector3 tempVec;
for (int j = 0; j < int(tVerts.size()); j++) {
if (tVerts[j].Position != pCur.Position &&
tVerts[j].Position != pPrev.Position &&
tVerts[j].Position != pNext.Position) {
tempVec = tVerts[j].Position;
break;
}
}
// Create a triangle from pCur, pPrev, pNext
for (int j = 0; j < int(iVerts.size()); j++) {
if (iVerts[j].Position == pPrev.Position) oIndices.push_back(j);
if (iVerts[j].Position == pNext.Position) oIndices.push_back(j);
if (iVerts[j].Position == tempVec) oIndices.push_back(j);
}
tVerts.clear();
break;
}
// If Vertex is not an interior vertex
float angle = math::AngleBetweenV3(pPrev.Position - pCur.Position,
pNext.Position - pCur.Position) *
(180 / 3.14159265359);
if (angle <= 0 && angle >= 180) continue;
// If any vertices are within this triangle
bool inTri = false;
for (int j = 0; j < int(iVerts.size()); j++) {
if (algorithm::inTriangle(iVerts[j].Position, pPrev.Position, pCur.Position,
pNext.Position) &&
iVerts[j].Position != pPrev.Position &&
iVerts[j].Position != pCur.Position &&
iVerts[j].Position != pNext.Position) {
inTri = true;
break;
}
}
if (inTri) continue;
// Create a triangle from pCur, pPrev, pNext
for (int j = 0; j < int(iVerts.size()); j++) {
if (iVerts[j].Position == pCur.Position) oIndices.push_back(j);
if (iVerts[j].Position == pPrev.Position) oIndices.push_back(j);
if (iVerts[j].Position == pNext.Position) oIndices.push_back(j);
}
// Delete pCur from the list
for (int j = 0; j < int(tVerts.size()); j++) {
if (tVerts[j].Position == pCur.Position) {
tVerts.erase(tVerts.begin() + j);
break;
}
}
// reset i to the start
// -1 since loop will add 1 to it
i = -1;
}
// if no triangles were created
if (oIndices.size() == 0) break;
// if no more vertices
if (tVerts.size() == 0) break;
}
}
// Load Materials from .mtl file
bool LoadMaterials(std::string path) {
// If the file is not a material file return false
if (path.substr(path.size() - 4, path.size()) != ".mtl") return false;
std::ifstream file(path);
// If the file is not found return false
if (!file.is_open()) return false;
Material tempMaterial;
bool listening = false;
// Go through each line looking for material variables
std::string curline;
while (std::getline(file, curline)) {
// new material and material name
if (algorithm::firstToken(curline) == "newmtl") {
if (!listening) {
listening = true;
if (curline.size() > 7) {
tempMaterial.name = algorithm::tail(curline);
} else {
tempMaterial.name = "none";
}
} else {
// Generate the material
// Push Back loaded Material
LoadedMaterials.push_back(tempMaterial);
// Clear Loaded Material
tempMaterial = Material();
if (curline.size() > 7) {
tempMaterial.name = algorithm::tail(curline);
} else {
tempMaterial.name = "none";
}
}
}
// Ambient Color
if (algorithm::firstToken(curline) == "Ka") {
std::vector<std::string> temp;
algorithm::split(algorithm::tail(curline), temp, " ");
if (temp.size() != 3) continue;
tempMaterial.Ka.X = std::stof(temp[0]);
tempMaterial.Ka.Y = std::stof(temp[1]);
tempMaterial.Ka.Z = std::stof(temp[2]);
}
// Diffuse Color
if (algorithm::firstToken(curline) == "Kd") {
std::vector<std::string> temp;
algorithm::split(algorithm::tail(curline), temp, " ");
if (temp.size() != 3) continue;
tempMaterial.Kd.X = std::stof(temp[0]);
tempMaterial.Kd.Y = std::stof(temp[1]);
tempMaterial.Kd.Z = std::stof(temp[2]);
}
// Specular Color
if (algorithm::firstToken(curline) == "Ks") {
std::vector<std::string> temp;
algorithm::split(algorithm::tail(curline), temp, " ");
if (temp.size() != 3) continue;
tempMaterial.Ks.X = std::stof(temp[0]);
tempMaterial.Ks.Y = std::stof(temp[1]);
tempMaterial.Ks.Z = std::stof(temp[2]);
}
// Specular Exponent
if (algorithm::firstToken(curline) == "Ns") {
tempMaterial.Ns = std::stof(algorithm::tail(curline));
}
// Optical Density
if (algorithm::firstToken(curline) == "Ni") {
tempMaterial.Ni = std::stof(algorithm::tail(curline));
}
// Dissolve
if (algorithm::firstToken(curline) == "d") {
tempMaterial.d = std::stof(algorithm::tail(curline));
}
// Illumination
if (algorithm::firstToken(curline) == "illum") {
tempMaterial.illum = std::stoi(algorithm::tail(curline));
}
// Ambient Texture Map
if (algorithm::firstToken(curline) == "map_Ka") {
tempMaterial.map_Ka = algorithm::tail(curline);
}
// Diffuse Texture Map
if (algorithm::firstToken(curline) == "map_Kd") {
tempMaterial.map_Kd = algorithm::tail(curline);
}
// Specular Texture Map
if (algorithm::firstToken(curline) == "map_Ks") {
tempMaterial.map_Ks = algorithm::tail(curline);
}
// Specular Hightlight Map
if (algorithm::firstToken(curline) == "map_Ns") {
tempMaterial.map_Ns = algorithm::tail(curline);
}
// Alpha Texture Map
if (algorithm::firstToken(curline) == "map_d") {
tempMaterial.map_d = algorithm::tail(curline);
}
// Bump Map
if (algorithm::firstToken(curline) == "map_Bump" ||
algorithm::firstToken(curline) == "map_bump" ||
algorithm::firstToken(curline) == "bump") {
tempMaterial.map_bump = algorithm::tail(curline);
}
}
// Deal with last material
// Push Back loaded Material
LoadedMaterials.push_back(tempMaterial);
// Test to see if anything was loaded
// If not return false
if (LoadedMaterials.empty()) return false;
// If so return true
else
return true;
}
};
} // namespace objl

View File

@ -0,0 +1,105 @@
#pragma once
#include "Gedeng/Mesh.h"
#include "Gedeng/Shader.h"
#include <glm/glm.hpp>
namespace Gedeng {
class DirectionalLight {
public:
glm::vec3 direction;
DirectionalLight() : shadow_shader(Gedeng::Shader("Shader/shadow.vs", "Shader/shadow.fs")) {
// Configure depth map
glGenFramebuffers(1, &depth_map_fbo);
// Create depth texture
glGenTextures(1, &depth_map);
glBindTexture(GL_TEXTURE_2D, depth_map);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// Attach depth texture as FBO's depth buffer
glBindFramebuffer(GL_FRAMEBUFFER, depth_map_fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_map, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
explicit DirectionalLight(glm::vec3 direction)
// TODO: Avoid all this duplication
: direction(direction), shadow_shader(Gedeng::Shader("Shader/shadow.vs", "Shader/shadow.fs")) {
// Configure depth map
glGenFramebuffers(1, &depth_map_fbo);
// Create depth texture
glGenTextures(1, &depth_map);
glBindTexture(GL_TEXTURE_2D, depth_map);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// Attach depth texture as FBO's depth buffer
glBindFramebuffer(GL_FRAMEBUFFER, depth_map_fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_map, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void set_in_shader(Shader &shader) {
shader.setVec3("light_direction", direction);
shader.setMat4("light_space_matrix", get_light_space_matrix());
}
void render_shadow(Mesh &mesh) {
shadow_shader.use();
shadow_shader.setMat4("lightSpaceMatrix", get_light_space_matrix());
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
glBindFramebuffer(GL_FRAMEBUFFER, depth_map_fbo);
mesh.render(shadow_shader);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
glm::mat4 get_light_space_matrix() {
// TODO: Make the values here configurable
float near_plane = 1.0f, far_plane = 100.0f;
glm::mat4 lightProjection = glm::ortho(-40.0f, 40.0f, -40.0f, 40.0f, near_plane, far_plane);
glm::mat4 lightView = glm::lookAt(-direction * 40.0f, direction, glm::vec3(0.0, 1.0, 0.0));
return lightProjection * lightView;
}
void clear_shadows() {
shadow_shader.use();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void bind_depth_map_to(int unit) {
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D, depth_map);
}
private:
unsigned int depth_map;
unsigned int depth_map_fbo;
const unsigned int SHADOW_WIDTH = 1024;
const unsigned int SHADOW_HEIGHT = 1024;
Gedeng::Shader shadow_shader;
};
} // namespace Gedeng

19
include/Gedeng/Material.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
namespace Gedeng {
class Material {
public:
Material() = default;
Material(float diffuse, float specular) : diffuse(diffuse), specular(specular) {
}
float diffuse = 0.8;
float specular = 0.2;
float normal_scale = 3.0;
};
} // namespace Gedeng

105
include/Gedeng/Mesh.h Normal file
View File

@ -0,0 +1,105 @@
#pragma once
// Must be the first include
#include <glad/glad.h>
// Other includes
#include <GLFW/glfw3.h>
#include "Gedeng/Shader.h"
#include "Gedeng/Spatial.h"
#include "Material.h"
#include <vector>
namespace Gedeng {
struct Mesh : public Spatial {
Mesh() = default;
explicit Mesh(const std::vector<float> &_vertices, const std::vector<unsigned int> &_indices)
: vertex_count(_indices.size()), vertices(_vertices), indices(_indices) {
// Copy the vertices into a local classic float array. Nothing was displayed without this,
// maybe
// due to weird hidden type incompatibility or out of scope issues?
float vertices[_vertices.size()];
std::copy(_vertices.begin(), _vertices.end(), vertices);
unsigned int indices[_indices.size()];
std::copy(_indices.begin(), _indices.end(), indices);
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then
// configure vertex attributes(s).
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
// Normal attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void *)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// texture coord attribute
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void *)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
// Tangent attribute
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void *)(8 * sizeof(float)));
glEnableVertexAttribArray(3);
// Bitangent attribute
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void *)(11 * sizeof(float)));
glEnableVertexAttribArray(4);
glBindVertexArray(0);
}
virtual void render(Shader &shader) const {
glm::mat4 model_matrix = get_matrix();
// model_matrix[3] = glm::vec4(get_origin(), 1.0);
shader.setMat4("model", model_matrix);
/* // 0 can't be a valid texture name, so we use it for meshes without textures here
if (texture_id != 0) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture_id);
}
if (normal_id != 0) {
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, normal_id);
} */
// TODO: Not always required (not when rendering shadows) - make functions separate?
// shader.setFloat("diffuseStrength", material.diffuse);
// shader.setFloat("specularStrength", material.specular);
// shader.setFloat("normalScale", material.normal_scale);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glDrawElements(GL_TRIANGLES, vertex_count, GL_UNSIGNED_INT, 0);
}
unsigned int vertex_count;
std::vector<float> vertices;
std::vector<unsigned int> indices;
private:
unsigned int EBO;
unsigned int VBO;
unsigned int VAO;
Material material;
};
} // namespace Gedeng

173
include/Gedeng/ObjMesh.h Normal file
View File

@ -0,0 +1,173 @@
#pragma once
#include "Mesh.h"
#include "vendor/OBJ_Loader.h"
#include <string>
namespace Gedeng {
class ObjMesh : public Mesh {
public:
struct Settings {
Settings() = default;
Settings(float minDistanceForRender, float maxDistanceForRender, bool colliding)
: minDistanceForRender(minDistanceForRender), maxDistanceForRender(maxDistanceForRender),
colliding(colliding) {
}
float minDistanceForRender = 0.0;
float maxDistanceForRender = 1000.0;
bool colliding = true;
};
explicit ObjMesh(const std::string &path, const Settings &settings)
: Mesh(getVerticesFromFile(path), getIndicesFromFile(path)), minDistance(settings.minDistanceForRender),
maxDistance(settings.maxDistanceForRender), colliding(settings.colliding) {
}
float minDistance;
float maxDistance;
bool colliding;
private:
static std::vector<float> getVerticesFromFile(const std::string &path) {
objl::Loader loader;
bool loadout = loader.LoadFile(path);
if (loadout) {
// TODO: Currently only the first mesh is used
objl::Mesh curMesh = loader.LoadedMeshes[0];
std::vector<float> vertexData = std::vector<float>();
auto tangents = getTangentsFromVertices(curMesh.Vertices, getIndicesFromFile(path));
for (int i = 0; i <= curMesh.Vertices.size(); i++) {
auto vertex = curMesh.Vertices[i];
auto tangent = tangents.first[i];
auto bitangent = tangents.second[i];
vertexData.emplace_back(vertex.Position.X);
vertexData.emplace_back(vertex.Position.Y);
vertexData.emplace_back(vertex.Position.Z);
vertexData.emplace_back(vertex.Normal.X);
vertexData.emplace_back(vertex.Normal.Y);
vertexData.emplace_back(vertex.Normal.Z);
vertexData.emplace_back(vertex.TextureCoordinate.X);
vertexData.emplace_back(vertex.TextureCoordinate.Y);
vertexData.emplace_back(tangent.x);
vertexData.emplace_back(tangent.y);
vertexData.emplace_back(tangent.z);
vertexData.emplace_back(bitangent.x);
vertexData.emplace_back(bitangent.y);
vertexData.emplace_back(bitangent.z);
}
return vertexData;
} else {
// Output Error
std::cout << "Failed to Load File. May have failed to find it or it was not an .obj file.\n";
}
}
static std::pair<std::vector<glm::vec3>, std::vector<glm::vec3>>
getTangentsFromVertices(const std::vector<objl::Vertex> &vertices, const std::vector<unsigned int> indices) {
// These vectors hold tangents and bitangents in the same way in which the vertices vectors
// holds vertices: Their index in the vector corresponds to indices in the indices vector.
std::vector<glm::vec3> tangents(vertices.size());
std::vector<glm::vec3> bitangents(vertices.size());
// Iterate over all triangles
for (uint index = 0; index < indices.size(); index += 3) {
// Index in vertex buffer
uint i0 = indices[index];
uint i1 = indices[index + 1];
uint i2 = indices[index + 2];
// Inputs
glm::vec3 vertex0 = glm::vec3(vertices[i0].Position.X, vertices[i0].Position.Y, vertices[i0].Position.Z);
glm::vec3 vertex1 = glm::vec3(vertices[i1].Position.X, vertices[i1].Position.Y, vertices[i1].Position.Z);
glm::vec3 vertex2 = glm::vec3(vertices[i2].Position.X, vertices[i2].Position.Y, vertices[i2].Position.Z);
glm::vec2 uv0 = glm::vec2(vertices[i0].TextureCoordinate.X, vertices[i0].TextureCoordinate.Y);
glm::vec2 uv1 = glm::vec2(vertices[i1].TextureCoordinate.X, vertices[i1].TextureCoordinate.Y);
glm::vec2 uv2 = glm::vec2(vertices[i2].TextureCoordinate.X, vertices[i2].TextureCoordinate.Y);
glm::vec3 normal0 = glm::vec3(vertices[i0].Normal.X, vertices[i0].Normal.Y, vertices[i0].Normal.Z);
glm::vec3 normal1 = glm::vec3(vertices[i1].Normal.X, vertices[i1].Normal.Y, vertices[i1].Normal.Z);
glm::vec3 normal2 = glm::vec3(vertices[i2].Normal.X, vertices[i2].Normal.Y, vertices[i2].Normal.Z);
// Edges of the triangle : position delta
glm::vec3 deltaPos1 = vertex1 - vertex0;
glm::vec3 deltaPos2 = vertex2 - vertex0;
// UV delta
glm::vec2 deltaUV1 = uv1 - uv0;
glm::vec2 deltaUV2 = uv2 - uv0;
float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
glm::vec3 tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y) * r;
glm::vec3 bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x) * r;
// Add the tangent and bitangent to the current value in the array.
// This is done to get an average in the end.
tangents[i0] += tangent;
tangents[i1] += tangent;
tangents[i2] += tangent;
bitangents[i0] += bitangent;
bitangents[i1] += bitangent;
bitangents[i2] += bitangent;
}
return std::pair<std::vector<glm::vec3>, std::vector<glm::vec3>>(tangents, bitangents);
}
static std::vector<unsigned int> getIndicesFromFile(const std::string &path) {
objl::Loader loader;
bool loadout = loader.LoadFile(path);
if (loadout) {
// TODO: Currently only the first mesh is used
objl::Mesh curMesh = loader.LoadedMeshes[0];
std::vector<unsigned int> indices = std::vector<unsigned int>();
// Emplace indices into the float vector
for (int j = 0; j < curMesh.Indices.size(); j++) {
indices.emplace_back(curMesh.Indices[j]);
}
return indices;
} else {
// Output Error
std::cout << "Failed to Load File. May have failed to find it or it was not an .obj file.\n";
}
}
// TODO: Only prints at the moment
static std::vector<float> getMaterialFromFile(const std::string &path) {
objl::Loader loader;
bool loadout = loader.LoadFile(path);
if (loadout) {
// TODO: Currently only the first mesh is used
objl::Mesh curMesh = loader.LoadedMeshes[0];
std::vector<float> vertexData = std::vector<float>();
return vertexData;
} else {
// Output Error
std::cout << "Failed to Load File. May have failed to find it or it was not an .obj file.\n";
}
}
};
} // namespace Gedeng

View File

@ -4,14 +4,14 @@
#include <glad/glad.h>
// Other includes
#include "Mesh.h"
#include "Shader.h"
#include "Spatial.h"
#include <GLFW/glfw3.h>
namespace Gedeng {
// A simple 2x2 quad mesh consisting of two triangles. Oriented upwards by default.
class QuadMesh : public Spatial {
class QuadMesh : public Mesh {
public:
QuadMesh(float scale = 1.0f) {
// Positions
@ -102,7 +102,7 @@ class QuadMesh : public Spatial {
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void *)(11 * sizeof(float)));
}
void render(Shader &shader) {
void render(Shader &shader) const override {
shader.setMat4("model", get_matrix());
glBindVertexArray(quadVAO);

167
test/shadow-demo/main.cpp Normal file
View File

@ -0,0 +1,167 @@
#include "Gedeng/DirectionalLight.h"
#include "Gedeng/Input.h"
#include "Gedeng/Logger.h"
#include "Gedeng/ObjMesh.h"
#include "Gedeng/ParticleSystem.h"
#include "Gedeng/QuadMesh.h"
#include "Gedeng/Shader.h"
#include "Gedeng/TextLabel.h"
#include "Gedeng/Vector3.h"
#include <GLFW/glfw3.h>
#define GEDENG_MAIN
#include <Gedeng.h>
// For Debugging
unsigned int quadVAO = 0;
unsigned int quadVBO;
void renderQuad() {
if (quadVAO == 0) {
float quadVertices[] = {
// positions // texture Coords
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
};
// setup plane VAO
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(3 * sizeof(float)));
}
glBindVertexArray(quadVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
}
class ShadowApp : public Gedeng::Application {
public:
ShadowApp(unsigned long ms_per_update, unsigned int window_size_x, unsigned int window_size_y,
Gedeng::String window_name)
: Application(ms_per_update, window_size_x, window_size_y, window_name), particle_interval(0.2),
number_of_steps(10.0), number_of_refinement_steps(10.0), bump_depth(0.1),
render_shader(Gedeng::Shader("Shader/bump.vs", "Shader/bump.fs")),
debug_shader(Gedeng::Shader("Shader/depth-debug.vs", "Shader/depth-debug.fs")),
camera(Gedeng::FPSCamera(90, 1920, 1080, 0.1, 1000.0)),
albedo("Resources/Textures/PavingStones/PavingStones070_2K_Color.jpg", Gedeng::Texture::Settings()),
bump("Resources/Textures/PavingStones/PavingStones070_2K_Displacement.jpg", Gedeng::Texture::Settings()),
normal("Resources/Textures/PavingStones/PavingStones070_2K_Normal.jpg", Gedeng::Texture::Settings()),
particle_tex1("Resources/Textures/Particles/circle.png", Gedeng::Texture::Settings()),
particle_tex2("Resources/Textures/Particles/magic.png", Gedeng::Texture::Settings()),
particle_tex3("Resources/Textures/Particles/smoke.png", Gedeng::Texture::Settings()),
quad_mesh(Gedeng::QuadMesh(10.0)), light(glm::vec3(0.57735, -0.57735, 0.57735)),
monkey_mesh("Resources/Meshes/Monkey.obj", Gedeng::ObjMesh::Settings()) {
particles.set_position(glm::vec3(0.0f, 0.0f, -10.0f));
particles.set_velocity(glm::vec3(-2, 4, -2), glm::vec3(2, 6, 2));
particles.set_gravity(glm::vec3(0, -4, 0));
particles.set_color(glm::vec3(0.0f, 0.5f, 1.0f));
particles.set_lifetime(1.0f, 1.5f);
particles.set_size(0.1);
particles.set_interval(particle_interval);
particles.set_number_of_particles(1);
particles.set_textures(&particle_tex1, &particle_tex2, &particle_tex3);
camera.translate(glm::vec3(0.0, 2.0, 1.0));
// camera.rotate(30, glm::vec3(1.0, 0.0, 0.0));
monkey_mesh.translate(glm::vec3(2.0, 3.0, -2.0));
}
~ShadowApp() = default;
void fixed_update(double delta) override {
camera.update(delta);
if (Gedeng::Input::is_mouse_down(GLFW_MOUSE_BUTTON_LEFT))
particles.set_position(camera.get_view_ray().get_plane_collision().collision_position);
if (Gedeng::Input::is_key_down(GLFW_KEY_2)) particle_interval = fmax(particle_interval + 0.1 * delta, 0.02);
if (Gedeng::Input::is_key_down(GLFW_KEY_1)) particle_interval = fmax(particle_interval - 0.1 * delta, 0.02);
particles.set_interval(particle_interval);
}
void dynamic_update(double delta) override {
// Shadows
light.clear_shadows();
light.render_shadow(monkey_mesh);
light.render_shadow(quad_mesh);
glClearColor(0.1, 0.1f, 0.1, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
render_shader.use();
// Camera
render_shader.setMat4("projection", camera.get_projection());
render_shader.setMat4("view", camera.get_view());
render_shader.setVec3("viewPos", camera.get_translation());
// Lighting
light.set_in_shader(render_shader);
render_shader.setFloat("number_of_steps", glm::max(0.0f, number_of_steps));
render_shader.setFloat("number_of_refinement_steps", glm::max(0.0f, number_of_refinement_steps));
render_shader.setFloat("bump_depth", glm::max(0.0f, bump_depth));
// Textures
albedo.bind_to(0);
normal.bind_to(1);
bump.bind_to(2);
light.bind_depth_map_to(3);
// Quad which is rendered onto
quad_mesh.render(render_shader);
// Particles
particles.set_camera(camera);
particles.update(delta);
particles.render();
// Props
render_shader.use();
monkey_mesh.render(render_shader);
/* // Render the light's depth map to a quad for debugging
debug_shader.use();
light.bind_depth_map_to(0);
renderQuad(); */
}
private:
float particle_interval;
float number_of_steps;
float number_of_refinement_steps;
float bump_depth;
Gedeng::Shader render_shader;
Gedeng::Shader debug_shader;
Gedeng::VertexBuffer vertex_rectangle;
Gedeng::FPSCamera camera;
Gedeng::Texture albedo;
Gedeng::Texture bump;
Gedeng::Texture normal;
Gedeng::Texture particle_tex1;
Gedeng::Texture particle_tex2;
Gedeng::Texture particle_tex3;
Gedeng::QuadMesh quad_mesh;
Gedeng::ParticleSystem particles;
Gedeng::DirectionalLight light;
Gedeng::ObjMesh monkey_mesh;
};
Gedeng::Application *Gedeng::create_application() {
GG_CLIENT_INFO("Creating Application");
return new ShadowApp(20, 1920, 1080, String("Parallax Demo"));
}