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/test-app.out', Glob('test/test-app/*.cpp'))
|
||||||
testEnv.Program('test/bin/parallax-demo.out', Glob('test/parallax-demo/*.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/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 {
|
in VS_OUT {
|
||||||
vec3 FragPos;
|
vec3 FragPos;
|
||||||
|
vec4 FragPosLightSpace;
|
||||||
vec2 TexCoords;
|
vec2 TexCoords;
|
||||||
vec3 TangentLightPos;
|
vec3 TangentLightDir;
|
||||||
vec3 TangentViewPos;
|
vec3 TangentViewPos;
|
||||||
vec3 TangentFragPos;
|
vec3 TangentFragPos;
|
||||||
} fs_in;
|
} fs_in;
|
||||||
@ -13,6 +14,7 @@ in VS_OUT {
|
|||||||
layout (binding = 0) uniform sampler2D albedoMap;
|
layout (binding = 0) uniform sampler2D albedoMap;
|
||||||
layout (binding = 1) uniform sampler2D normalMap;
|
layout (binding = 1) uniform sampler2D normalMap;
|
||||||
layout (binding = 2) uniform sampler2D depthMap;
|
layout (binding = 2) uniform sampler2D depthMap;
|
||||||
|
layout (binding = 3) uniform sampler2D shadowMap;
|
||||||
|
|
||||||
uniform float bump_depth;
|
uniform float bump_depth;
|
||||||
uniform float number_of_steps;
|
uniform float number_of_steps;
|
||||||
@ -57,6 +59,28 @@ vec2 get_parallax_offset_uv(vec2 uv, vec3 view_direction) {
|
|||||||
return current_uv;
|
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() {
|
void main() {
|
||||||
// Offset texture coordinates with Parallax Mapping
|
// Offset texture coordinates with Parallax Mapping
|
||||||
vec3 view_direction = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos);
|
vec3 view_direction = normalize(fs_in.TangentViewPos - fs_in.TangentFragPos);
|
||||||
@ -80,7 +104,7 @@ void main() {
|
|||||||
vec3 ambient = 0.1 * color;
|
vec3 ambient = 0.1 * color;
|
||||||
|
|
||||||
// Apply albedo with intensity based on the dot product between the light direction and the normal here
|
// 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);
|
float light_normal_dot = max(dot(light_direction, normal), 0.0);
|
||||||
vec3 albedo = light_normal_dot * color;
|
vec3 albedo = light_normal_dot * color;
|
||||||
|
|
||||||
@ -90,7 +114,9 @@ void main() {
|
|||||||
|
|
||||||
vec3 specular = vec3(0.2) * spec;
|
vec3 specular = vec3(0.2) * spec;
|
||||||
|
|
||||||
|
float shadow = get_shadow(fs_in.FragPosLightSpace, normal);
|
||||||
|
|
||||||
// Apply
|
// 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 {
|
out VS_OUT {
|
||||||
vec3 FragPos;
|
vec3 FragPos;
|
||||||
|
vec4 FragPosLightSpace;
|
||||||
vec2 TexCoords;
|
vec2 TexCoords;
|
||||||
vec3 TangentLightPos;
|
vec3 TangentLightDir;
|
||||||
vec3 TangentViewPos;
|
vec3 TangentViewPos;
|
||||||
vec3 TangentFragPos;
|
vec3 TangentFragPos;
|
||||||
} vs_out;
|
} vs_out;
|
||||||
@ -18,20 +19,23 @@ uniform mat4 projection;
|
|||||||
uniform mat4 view;
|
uniform mat4 view;
|
||||||
uniform mat4 model;
|
uniform mat4 model;
|
||||||
|
|
||||||
uniform vec3 lightPos;
|
uniform vec3 light_direction;
|
||||||
|
uniform mat4 light_space_matrix;
|
||||||
|
|
||||||
uniform vec3 viewPos;
|
uniform vec3 viewPos;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = projection * view * model * vec4(aPos, 1.0);
|
gl_Position = projection * view * model * vec4(aPos, 1.0);
|
||||||
vs_out.FragPos = vec3(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 T = normalize(mat3(model) * aTangent);
|
||||||
vec3 B = normalize(mat3(model) * aBitangent);
|
vec3 B = normalize(mat3(model) * aBitangent);
|
||||||
vec3 N = normalize(mat3(model) * aNormal);
|
vec3 N = normalize(mat3(model) * aNormal);
|
||||||
mat3 TBN = transpose(mat3(T, B, N));
|
mat3 TBN = transpose(mat3(T, B, N));
|
||||||
|
|
||||||
vs_out.TangentLightPos = TBN * lightPos;
|
vs_out.TangentLightDir = light_direction;
|
||||||
vs_out.TangentViewPos = TBN * viewPos;
|
vs_out.TangentViewPos = TBN * viewPos;
|
||||||
vs_out.TangentFragPos = TBN * vs_out.FragPos;
|
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);
|
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
|
||||||
|
|
||||||
// FIXME: Disabled because of a bug with particles: they're discarded as if the floor moves with the camera
|
// 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);
|
glEnable(GL_BLEND);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
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>
|
#include <glad/glad.h>
|
||||||
|
|
||||||
// Other includes
|
// Other includes
|
||||||
|
#include "Mesh.h"
|
||||||
#include "Shader.h"
|
#include "Shader.h"
|
||||||
#include "Spatial.h"
|
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
namespace Gedeng {
|
namespace Gedeng {
|
||||||
|
|
||||||
// A simple 2x2 quad mesh consisting of two triangles. Oriented upwards by default.
|
// A simple 2x2 quad mesh consisting of two triangles. Oriented upwards by default.
|
||||||
class QuadMesh : public Spatial {
|
class QuadMesh : public Mesh {
|
||||||
public:
|
public:
|
||||||
QuadMesh(float scale = 1.0f) {
|
QuadMesh(float scale = 1.0f) {
|
||||||
// Positions
|
// Positions
|
||||||
@ -102,7 +102,7 @@ class QuadMesh : public Spatial {
|
|||||||
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), (void *)(11 * sizeof(float)));
|
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());
|
shader.setMat4("model", get_matrix());
|
||||||
|
|
||||||
glBindVertexArray(quadVAO);
|
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