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:
parent
eea0c57e7c
commit
3c35a730f5
@ -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'))
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
21
Shader/depth-debug.fs
Normal 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
13
Shader/depth-debug.vs
Normal 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
7
Shader/shadow.fs
Normal file
@ -0,0 +1,7 @@
|
||||
#version 430
|
||||
|
||||
void main()
|
||||
{
|
||||
// This happens implicitly anyways
|
||||
// gl_FragDepth = gl_FragCoord.z;
|
||||
}
|
10
Shader/shadow.vs
Normal file
10
Shader/shadow.vs
Normal 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);
|
||||
}
|
@ -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
977
cpp/vendor/OBJ_Loader.h
vendored
Normal 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
|
105
include/Gedeng/DirectionalLight.h
Normal file
105
include/Gedeng/DirectionalLight.h
Normal 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
19
include/Gedeng/Material.h
Normal 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
105
include/Gedeng/Mesh.h
Normal 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
173
include/Gedeng/ObjMesh.h
Normal 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
|
@ -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
167
test/shadow-demo/main.cpp
Normal 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"));
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user