diff --git a/ECS/Components/Camera.h b/ECS/Components/Camera.h index f4be5c2..42f5999 100644 --- a/ECS/Components/Camera.h +++ b/ECS/Components/Camera.h @@ -9,13 +9,14 @@ #include struct Camera { - /// Create a camera with a field of view (in degrees), width and height (in any unit) and near and far distances - Camera(float fov, float width, float height, float near, float far) : projection( - glm::perspective(glm::radians(fov), width / height, near, far)) {} + /// Create a camera with a field of view (in degrees), width and height (in any unit) and near + /// and far distances + Camera(float fov, float width, float height, float near, float far) + : projection(glm::perspective(glm::radians(fov), width / height, near, far)) {} glm::mat4 projection; glm::mat4 view; }; -#endif //ECSGAME_CAMERA_H +#endif // ECSGAME_CAMERA_H diff --git a/ECS/Components/DirectionalLight.h b/ECS/Components/DirectionalLight.h index 97bf2c1..96f14f1 100644 --- a/ECS/Components/DirectionalLight.h +++ b/ECS/Components/DirectionalLight.h @@ -5,10 +5,12 @@ #ifndef ECSGAME_DIRECTIONALLIGHT_H #define ECSGAME_DIRECTIONALLIGHT_H +#include + struct DirectionalLight { explicit DirectionalLight(const glm::vec3 &direction) : direction(direction) {} glm::vec3 direction; }; -#endif //ECSGAME_DIRECTIONALLIGHT_H +#endif // ECSGAME_DIRECTIONALLIGHT_H diff --git a/ECS/Components/LODObjMesh.h b/ECS/Components/LODObjMesh.h index 460ceec..f6d71c7 100644 --- a/ECS/Components/LODObjMesh.h +++ b/ECS/Components/LODObjMesh.h @@ -5,9 +5,9 @@ #ifndef ECSGAME_LODOBJMESH_H #define ECSGAME_LODOBJMESH_H +#include "ObjMesh.h" #include #include -#include "ObjMesh.h" struct LODObjMesh { explicit LODObjMesh(std::vector meshes) : meshes(std::move(meshes)) {} @@ -15,4 +15,4 @@ struct LODObjMesh { std::vector meshes; }; -#endif //ECSGAME_LODOBJMESH_H +#endif // ECSGAME_LODOBJMESH_H diff --git a/ECS/Components/Mesh.h b/ECS/Components/Mesh.h index c5794aa..c840b9b 100644 --- a/ECS/Components/Mesh.h +++ b/ECS/Components/Mesh.h @@ -5,13 +5,15 @@ #ifndef ECSGAME_MESH_H #define ECSGAME_MESH_H -#include #include +#include #include struct Mesh { - explicit Mesh(const std::vector &_vertices, const std::vector &_indices) : vertex_count(_indices.size()) { - // Copy the vertices into a local classic float array. Nothing was displayed without this, maybe + explicit Mesh(const std::vector &_vertices, const std::vector &_indices) + : vertex_count(_indices.size()) { + // 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); @@ -23,7 +25,8 @@ struct Mesh { 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). + // 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); @@ -33,23 +36,27 @@ struct Mesh { 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); + 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))); + 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))); + 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))); + 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))); + glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 14 * sizeof(float), + (void *)(11 * sizeof(float))); glEnableVertexAttribArray(4); glBindVertexArray(0); @@ -61,11 +68,11 @@ struct Mesh { glDrawElements(GL_TRIANGLES, vertex_count, GL_UNSIGNED_INT, 0); } -private: + private: unsigned int EBO; unsigned int VBO; unsigned int VAO; unsigned int vertex_count; }; -#endif //ECSGAME_MESH_H +#endif // ECSGAME_MESH_H diff --git a/ECS/Components/MouseLook.h b/ECS/Components/MouseLook.h index 8c2c05c..b98d072 100644 --- a/ECS/Components/MouseLook.h +++ b/ECS/Components/MouseLook.h @@ -5,6 +5,9 @@ #ifndef ECSGAME_MOUSELOOK_H #define ECSGAME_MOUSELOOK_H +#include +#include + struct MouseLook { explicit MouseLook(float sensitivity) : sensitivity(sensitivity) {} @@ -18,4 +21,4 @@ struct MouseLook { bool is_active = true; }; -#endif //ECSGAME_MOUSELOOK_H +#endif // ECSGAME_MOUSELOOK_H diff --git a/ECS/Components/Movement.h b/ECS/Components/Movement.h index a72a976..bd11d9b 100644 --- a/ECS/Components/Movement.h +++ b/ECS/Components/Movement.h @@ -5,6 +5,8 @@ #ifndef ECSGAME_MOVEMENT_H #define ECSGAME_MOVEMENT_H +#include + struct Movement { Movement(glm::vec3 speed) : speed(speed) {} @@ -17,4 +19,4 @@ struct Movement { bool is_active = true; }; -#endif //ECSGAME_MOVEMENT_H +#endif // ECSGAME_MOVEMENT_H diff --git a/ECS/Components/ObjMesh.h b/ECS/Components/ObjMesh.h index 2b26374..85ecc43 100644 --- a/ECS/Components/ObjMesh.h +++ b/ECS/Components/ObjMesh.h @@ -5,29 +5,31 @@ #ifndef ECSGAME_OBJMESH_H #define ECSGAME_OBJMESH_H -#include "Mesh.h" #include "../../Util/OBJ_Loader.h" +#include "Mesh.h" struct ObjMesh : public Mesh { struct Settings { Settings() = default; - Settings(float minDistanceForRender, float maxDistanceForRender, float diffuse, float specular) - : minDistanceForRender(minDistanceForRender), maxDistanceForRender(maxDistanceForRender) {} + Settings(float minDistanceForRender, float maxDistanceForRender, float diffuse, + float specular) + : minDistanceForRender(minDistanceForRender), + maxDistanceForRender(maxDistanceForRender) {} float minDistanceForRender = 0.0; float maxDistanceForRender = 1000.0; }; - explicit ObjMesh(const std::string &path, const Settings &settings) : Mesh(getVerticesFromFile(path), getIndicesFromFile(path)), - minDistance(settings.minDistanceForRender), - maxDistance(settings.maxDistanceForRender) {} + explicit ObjMesh(const std::string &path, const Settings &settings) + : Mesh(getVerticesFromFile(path), getIndicesFromFile(path)), + minDistance(settings.minDistanceForRender), maxDistance(settings.maxDistanceForRender) {} float minDistance; float maxDistance; -private: + private: static std::vector getVerticesFromFile(const std::string &path) { objl::Loader loader; @@ -65,13 +67,16 @@ private: 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"; + 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> getTangentsFromVertices(const std::vector &vertices, const std::vector 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. + static std::pair, std::vector> + getTangentsFromVertices(const std::vector &vertices, + const std::vector 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 tangents(vertices.size()); std::vector bitangents(vertices.size()); @@ -83,17 +88,26 @@ private: 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::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::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); + 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; @@ -104,8 +118,8 @@ private: 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; + 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. @@ -140,7 +154,8 @@ private: 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"; + std::cout + << "Failed to Load File. May have failed to find it or it was not an .obj file.\n"; } } @@ -158,12 +173,12 @@ private: // Print Material std::cout << "Material: " << curMesh.MeshMaterial.name << "\n"; - std::cout << "Ambient Color: " << curMesh.MeshMaterial.Ka.X << ", " << curMesh.MeshMaterial.Ka.Y << ", " - << curMesh.MeshMaterial.Ka.Z << "\n"; - std::cout << "Diffuse Color: " << curMesh.MeshMaterial.Kd.X << ", " << curMesh.MeshMaterial.Kd.Y << ", " - << curMesh.MeshMaterial.Kd.Z << "\n"; - std::cout << "Specular Color: " << curMesh.MeshMaterial.Ks.X << ", " << curMesh.MeshMaterial.Ks.Y << ", " - << curMesh.MeshMaterial.Ks.Z << "\n"; + std::cout << "Ambient Color: " << curMesh.MeshMaterial.Ka.X << ", " + << curMesh.MeshMaterial.Ka.Y << ", " << curMesh.MeshMaterial.Ka.Z << "\n"; + std::cout << "Diffuse Color: " << curMesh.MeshMaterial.Kd.X << ", " + << curMesh.MeshMaterial.Kd.Y << ", " << curMesh.MeshMaterial.Kd.Z << "\n"; + std::cout << "Specular Color: " << curMesh.MeshMaterial.Ks.X << ", " + << curMesh.MeshMaterial.Ks.Y << ", " << curMesh.MeshMaterial.Ks.Z << "\n"; std::cout << "Specular Exponent: " << curMesh.MeshMaterial.Ns << "\n"; std::cout << "Optical Density: " << curMesh.MeshMaterial.Ni << "\n"; std::cout << "Dissolve: " << curMesh.MeshMaterial.d << "\n"; @@ -177,9 +192,10 @@ private: 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"; + std::cout + << "Failed to Load File. May have failed to find it or it was not an .obj file.\n"; } } }; -#endif //ECSGAME_OBJMESH_H +#endif // ECSGAME_OBJMESH_H diff --git a/ECS/Components/PathMove.h b/ECS/Components/PathMove.h index 39a904c..3ea9d17 100644 --- a/ECS/Components/PathMove.h +++ b/ECS/Components/PathMove.h @@ -26,9 +26,7 @@ struct PathMove { struct Views { Views(std::vector views) : views(views) {} - void add_view(glm::quat new_quat) { - views.emplace_back(new_quat); - } + void add_view(glm::quat new_quat) { views.emplace_back(new_quat); } std::vector views; }; @@ -40,7 +38,8 @@ struct PathMove { float time_passed = 0.0; int current_point_index = 0; - int speed_addition = 0; // 0, -1 or 1 depending on whether the speed should stay, decrease or increase + int speed_addition = + 0; // 0, -1 or 1 depending on whether the speed should stay, decrease or increase Path path; Views views; diff --git a/ECS/Components/SineAnimation.h b/ECS/Components/SineAnimation.h index 38badc0..5955a22 100644 --- a/ECS/Components/SineAnimation.h +++ b/ECS/Components/SineAnimation.h @@ -5,11 +5,14 @@ #ifndef ECSGAME_SINEANIMATION_H #define ECSGAME_SINEANIMATION_H +#include + struct SineAnimation { - SineAnimation(const glm::vec3 &maxDistance, float speedScale) : maxDistance(maxDistance), speedScale(speedScale) {} + SineAnimation(const glm::vec3 &maxDistance, float speedScale) + : maxDistance(maxDistance), speedScale(speedScale) {} glm::vec3 maxDistance; float speedScale; }; -#endif //ECSGAME_SINEANIMATION_H +#endif // ECSGAME_SINEANIMATION_H diff --git a/ECS/Components/Texture.h b/ECS/Components/Texture.h index 611b24d..09d7a8a 100644 --- a/ECS/Components/Texture.h +++ b/ECS/Components/Texture.h @@ -19,7 +19,7 @@ struct Texture { }; // Bind a texture from the given path and return its ID - uint loadTexture(const std::string& path, Settings settings) { + uint loadTexture(const std::string &path, Settings settings) { uint gl_id; glGenTextures(1, &gl_id); @@ -41,15 +41,12 @@ struct Texture { if (data) { // Alpha channel? unsigned int glChannels = GL_RGB; - if (nrChannels == 4) { - glChannels = GL_RGBA; - } + if (nrChannels == 4) { glChannels = GL_RGBA; } - glTexImage2D(GL_TEXTURE_2D, 0, glChannels, width, height, 0, glChannels, GL_UNSIGNED_BYTE, data); + glTexImage2D(GL_TEXTURE_2D, 0, glChannels, width, height, 0, glChannels, + GL_UNSIGNED_BYTE, data); - if (settings.mipmaps) { - glGenerateMipmap(GL_TEXTURE_2D); - } + if (settings.mipmaps) { glGenerateMipmap(GL_TEXTURE_2D); } } else { std::cout << "Failed to load texture" << std::endl; } @@ -59,11 +56,12 @@ struct Texture { return gl_id; } - explicit Texture(const std::string& path, Settings settings, bool transparent) : id(loadTexture(path, settings)), render_transparent(transparent) {} + explicit Texture(const std::string &path, Settings settings, bool transparent) + : id(loadTexture(path, settings)), render_transparent(transparent) {} - void addNormalmap(const std::string& path, Settings settings) { - normal_id = loadTexture(path, settings); + void addNormalmap(const std::string &path, Settings settings) { + normal_id = loadTexture(path, settings); } }; -#endif //ECSGAME_TEXTURE_H +#endif // ECSGAME_TEXTURE_H diff --git a/ECS/Components/Transform.h b/ECS/Components/Transform.h index c2351db..451c847 100644 --- a/ECS/Components/Transform.h +++ b/ECS/Components/Transform.h @@ -17,33 +17,21 @@ struct Transform { glm::mat4 matrix = glm::mat4(1.0f); // Initialize as identity glm::vec3 origin = glm::vec3(0.0f, 0.0f, 0.0f); - void translate(glm::vec3 offset) { - matrix = glm::translate(matrix, offset); - } + void translate(glm::vec3 offset) { matrix = glm::translate(matrix, offset); } - glm::vec3 get_translation() { - return glm::vec3(matrix[3]); - } + glm::vec3 get_translation() { return glm::vec3(matrix[3]); } - void uniform_scale(float factor) { - scale(glm::vec3(factor, factor, factor)); - } + void uniform_scale(float factor) { scale(glm::vec3(factor, factor, factor)); } - void scale(glm::vec3 factors) { - matrix = glm::scale(matrix, factors); - } + void scale(glm::vec3 factors) { matrix = glm::scale(matrix, factors); } void rotate(float degrees, glm::vec3 axis) { matrix = glm::rotate(matrix, glm::radians(degrees), axis); } - void set_origin(glm::vec3 position) { - origin = position; - } + void set_origin(glm::vec3 position) { origin = position; } - void add_to_origin(glm::vec3 addition) { - origin += addition; - } + void add_to_origin(glm::vec3 addition) { origin += addition; } void set_rotation_from_quat(glm::quat quaternion) { // Remember translation @@ -53,21 +41,13 @@ struct Transform { matrix[3] = save; } - glm::vec3 get_origin() const { - return origin; - } + glm::vec3 get_origin() const { return origin; } - glm::vec3 forward() const { - return matrix * glm::vec4(0.0, 0.0, -1.0, 0.0); - } + glm::vec3 forward() const { return matrix * glm::vec4(0.0, 0.0, -1.0, 0.0); } - glm::vec3 up() const { - return matrix * glm::vec4(0.0, 1.0, 0.0, 0.0); - } + glm::vec3 up() const { return matrix * glm::vec4(0.0, 1.0, 0.0, 0.0); } - glm::vec3 right() const { - return matrix * glm::vec4(1.0, 0.0, 0.0, 0.0); - } + glm::vec3 right() const { return matrix * glm::vec4(1.0, 0.0, 0.0, 0.0); } }; -#endif //ECSGAME_TRANSFORM_H +#endif // ECSGAME_TRANSFORM_H diff --git a/ECS/Events/InputEvent.h b/ECS/Events/InputEvent.h index 38b5c57..b2ef99c 100644 --- a/ECS/Events/InputEvent.h +++ b/ECS/Events/InputEvent.h @@ -10,4 +10,4 @@ struct InputEvent { int action; }; -#endif //ECSGAME_INPUTEVENT_H +#endif // ECSGAME_INPUTEVENT_H diff --git a/ECS/Events/MouseMoveEvent.h b/ECS/Events/MouseMoveEvent.h index e9405ba..b6d2f24 100644 --- a/ECS/Events/MouseMoveEvent.h +++ b/ECS/Events/MouseMoveEvent.h @@ -10,4 +10,4 @@ struct MouseMoveEvent { double newY; }; -#endif //ECSGAME_INPUTEVENT_H +#endif // ECSGAME_INPUTEVENT_H diff --git a/ECS/Systems/GravitySystem.h b/ECS/Systems/GravitySystem.h index 05b249c..cc8f1f7 100644 --- a/ECS/Systems/GravitySystem.h +++ b/ECS/Systems/GravitySystem.h @@ -5,16 +5,14 @@ #ifndef ECSGAME_GRAVITYSYSTEM_H #define ECSGAME_GRAVITYSYSTEM_H -#include "../ECS.h" #include "../Components/Transform.h" +#include "../ECS.h" using namespace ECS; class GravitySystem : public EntitySystem { -public: - explicit GravitySystem(float amount) { - gravityAmount = amount; - } + public: + explicit GravitySystem(float amount) { gravityAmount = amount; } void tick(World *pWorld, float deltaTime) override { pWorld->each([&](Entity *ent, ComponentHandle position) { @@ -22,8 +20,8 @@ public: }); } -private: + private: float gravityAmount; }; -#endif //ECSGAME_GRAVITYSYSTEM_H +#endif // ECSGAME_GRAVITYSYSTEM_H diff --git a/ECS/Systems/InteractivePathSystem.h b/ECS/Systems/InteractivePathSystem.h index dd486db..814e071 100644 --- a/ECS/Systems/InteractivePathSystem.h +++ b/ECS/Systems/InteractivePathSystem.h @@ -1,16 +1,16 @@ #ifndef __PATHMOVEMENTSWITCHSYSTEM_H__ #define __PATHMOVEMENTSWITCHSYSTEM_H__ -#include #include +#include #include -#include "../ECS.h" +#include "../Components/MouseLook.h" +#include "../Components/Movement.h" #include "../Components/PathMove.h" +#include "../ECS.h" #include "../Events/InputEvent.h" -#include "../Components/Movement.h" -#include "../Components/MouseLook.h" using namespace ECS; @@ -23,36 +23,39 @@ class InteractivePathSystem : public EntitySystem, public EventSubscribereach([&](Entity *ent, ComponentHandle pathmove, ComponentHandle movement, ComponentHandle mouselook) { - if (event.action == GLFW_PRESS) { - // Switch between them - if (pathmove->is_active) { - pathmove->is_active = false; - movement->is_active = true; - mouselook->is_active = true; - } else { - pathmove->is_active = true; - movement->is_active = false; - mouselook->is_active = false; + myWorld->each( + [&](Entity *ent, ComponentHandle pathmove, + ComponentHandle movement, ComponentHandle mouselook) { + if (event.action == GLFW_PRESS) { + // Switch between them + if (pathmove->is_active) { + pathmove->is_active = false; + movement->is_active = true; + mouselook->is_active = true; + } else { + pathmove->is_active = true; + movement->is_active = false; + mouselook->is_active = false; + } } - } - }); + }); } else if (event.key == GLFW_KEY_R) { - myWorld->each([&](Entity *ent, ComponentHandle pathmove, ComponentHandle movement, ComponentHandle mouselook, ComponentHandle transform) { - if (event.action == GLFW_PRESS) { - // Add this point to the path - pathmove->path.add_point(transform->origin); - pathmove->views.add_view(mouselook->rotation); - } - }); + myWorld->each( + [&](Entity *ent, ComponentHandle pathmove, + ComponentHandle movement, ComponentHandle mouselook, + ComponentHandle transform) { + if (event.action == GLFW_PRESS) { + // Add this point to the path + pathmove->path.add_point(transform->origin); + pathmove->views.add_view(mouselook->rotation); + } + }); } } - void unconfigure(World *pWorld) override { - pWorld->unsubscribeAll(this); - } + void unconfigure(World *pWorld) override { pWorld->unsubscribeAll(this); } -private: + private: World *myWorld; }; diff --git a/ECS/Systems/KeyboardMovementSystem.h b/ECS/Systems/KeyboardMovementSystem.h index 5297315..d3b0446 100644 --- a/ECS/Systems/KeyboardMovementSystem.h +++ b/ECS/Systems/KeyboardMovementSystem.h @@ -5,15 +5,15 @@ #ifndef ECSGAME_KEYBOARDMOVEMENTSYSTEM_H #define ECSGAME_KEYBOARDMOVEMENTSYSTEM_H -#include #include +#include #include -#include "../ECS.h" +#include "../Components/Movement.h" #include "../Components/Transform.h" +#include "../ECS.h" #include "../Events/InputEvent.h" -#include "../Components/Movement.h" using namespace ECS; @@ -61,20 +61,20 @@ class KeyboardMovementSystem : public EntitySystem, public EventSubscribereach( - [&](Entity *ent, ComponentHandle transform, ComponentHandle movement) { - if (!movement->is_active) return; + pWorld->each([&](Entity *ent, ComponentHandle transform, + ComponentHandle movement) { + if (!movement->is_active) return; - transform->add_to_origin(transform->matrix * glm::vec4((glm::vec3(movement->moving) * movement->speed * deltaTime), 0.0)); - }); + transform->add_to_origin( + transform->matrix * + glm::vec4((glm::vec3(movement->moving) * movement->speed * deltaTime), 0.0)); + }); } - void unconfigure(World *pWorld) override { - pWorld->unsubscribeAll(this); - } + void unconfigure(World *pWorld) override { pWorld->unsubscribeAll(this); } -private: + private: World *myWorld; }; -#endif //ECSGAME_KEYBOARDMOVEMENTSYSTEM_H +#endif // ECSGAME_KEYBOARDMOVEMENTSYSTEM_H diff --git a/ECS/Systems/MouseLookSystem.h b/ECS/Systems/MouseLookSystem.h index 94a10b6..b3a02d7 100644 --- a/ECS/Systems/MouseLookSystem.h +++ b/ECS/Systems/MouseLookSystem.h @@ -5,15 +5,16 @@ #ifndef ECSGAME_MOUSELOOKSYSTEM_H #define ECSGAME_MOUSELOOKSYSTEM_H -#include "../ECS.h" -#include "../Components/Transform.h" +#include "../Components/Camera.h" #include "../Components/MouseLook.h" +#include "../Components/Transform.h" +#include "../ECS.h" #include "../Events/MouseMoveEvent.h" using namespace ECS; -class MouseLookSystem : public EntitySystem, public EventSubscriber { -public: +class MouseLookSystem : public EntitySystem, public EventSubscriber { + public: explicit MouseLookSystem(int width, int height) : lastX(width / 2.0), lastY(height / 2.0) {} void configure(World *pWorld) override { @@ -22,44 +23,46 @@ public: myWorld->subscribe(this); } - void unconfigure(World *pWorld) override { - pWorld->unsubscribeAll(this); - } + void unconfigure(World *pWorld) override { pWorld->unsubscribeAll(this); } void receive(World *pWorld, const MouseMoveEvent &event) override { - pWorld->each([&](Entity *ent, ComponentHandle transform, ComponentHandle mouse, ComponentHandle camera) { - double xOffset = lastX - event.newX; - double yOffset = lastY - event.newY; + pWorld->each( + [&](Entity *ent, ComponentHandle transform, ComponentHandle mouse, + ComponentHandle camera) { + double xOffset = lastX - event.newX; + double yOffset = lastY - event.newY; - lastX = event.newX; - lastY = event.newY; + lastX = event.newX; + lastY = event.newY; - mouse->yaw += xOffset * mouse->sensitivity; - mouse->pitch += yOffset * mouse->sensitivity; - }); + mouse->yaw += xOffset * mouse->sensitivity; + mouse->pitch += yOffset * mouse->sensitivity; + }); } void tick(World *pWorld, float deltaTime) override { - pWorld->each([&](Entity *ent, ComponentHandle transform, ComponentHandle mouse, ComponentHandle camera) { - if (!mouse->is_active) return; + pWorld->each( + [&](Entity *ent, ComponentHandle transform, ComponentHandle mouse, + ComponentHandle camera) { + if (!mouse->is_active) return; - if(mouse->pitch > 80.0f) - mouse->pitch = 80.0f; - if(mouse->pitch < -80.0f) - mouse->pitch = -80.0f; + if (mouse->pitch > 80.0f) mouse->pitch = 80.0f; + if (mouse->pitch < -80.0f) mouse->pitch = -80.0f; - mouse->rotation = glm::angleAxis(glm::radians((float)mouse->yaw), glm::vec3(0.f, 1.f, 0.f)); - mouse->rotation *= glm::angleAxis(glm::radians((float)mouse->pitch), glm::vec3(1.f, 0.f, 0.f)); + mouse->rotation = + glm::angleAxis(glm::radians((float)mouse->yaw), glm::vec3(0.f, 1.f, 0.f)); + mouse->rotation *= + glm::angleAxis(glm::radians((float)mouse->pitch), glm::vec3(1.f, 0.f, 0.f)); - transform->set_rotation_from_quat(mouse->rotation); - }); + transform->set_rotation_from_quat(mouse->rotation); + }); } -private: + private: double lastX; double lastY; World *myWorld; }; -#endif //ECSGAME_MOUSELOOKSYSTEM_H +#endif // ECSGAME_MOUSELOOKSYSTEM_H diff --git a/ECS/Systems/PathMoveSystem.h b/ECS/Systems/PathMoveSystem.h index 27992e0..250b164 100644 --- a/ECS/Systems/PathMoveSystem.h +++ b/ECS/Systems/PathMoveSystem.h @@ -1,15 +1,15 @@ #ifndef __PATHMOVESYSTEM_H__ #define __PATHMOVESYSTEM_H__ -#include #include +#include #include -#include "../ECS.h" -#include "../Components/Transform.h" -#include "../Events/InputEvent.h" #include "../Components/PathMove.h" +#include "../Components/Transform.h" +#include "../ECS.h" +#include "../Events/InputEvent.h" using namespace ECS; @@ -19,7 +19,7 @@ using namespace ECS; float get_t(float alpha, float t, glm::vec3 p0, glm::vec3 p1) { float a = pow((p1.x - p0.x), 2.0f) + pow((p1.y - p0.y), 2.0f) + pow((p1.z - p0.z), 2.0f); float b = pow(a, alpha * 0.5f); - + return (b + t); } @@ -34,14 +34,14 @@ glm::vec3 catmul(float alpha, glm::vec3 p0, glm::vec3 p1, glm::vec3 p2, glm::vec // Lerp t to be between t1 and t2 t = t1 + t * (t2 - t1); - glm::vec3 A1 = (t1-t)/(t1-t0)*p0 + (t-t0)/(t1-t0)*p1; - glm::vec3 A2 = (t2-t)/(t2-t1)*p1 + (t-t1)/(t2-t1)*p2; - glm::vec3 A3 = (t3-t)/(t3-t2)*p2 + (t-t2)/(t3-t2)*p3; - - glm::vec3 B1 = (t2-t)/(t2-t0)*A1 + (t-t0)/(t2-t0)*A2; - glm::vec3 B2 = (t3-t)/(t3-t1)*A2 + (t-t1)/(t3-t1)*A3; - - glm::vec3 C = (t2-t)/(t2-t1)*B1 + (t-t1)/(t2-t1)*B2; + glm::vec3 A1 = (t1 - t) / (t1 - t0) * p0 + (t - t0) / (t1 - t0) * p1; + glm::vec3 A2 = (t2 - t) / (t2 - t1) * p1 + (t - t1) / (t2 - t1) * p2; + glm::vec3 A3 = (t3 - t) / (t3 - t2) * p2 + (t - t2) / (t3 - t2) * p3; + + glm::vec3 B1 = (t2 - t) / (t2 - t0) * A1 + (t - t0) / (t2 - t0) * A2; + glm::vec3 B2 = (t3 - t) / (t3 - t1) * A2 + (t - t1) / (t3 - t1) * A3; + + glm::vec3 C = (t2 - t) / (t2 - t1) * B1 + (t - t1) / (t2 - t1) * B2; return C; } @@ -75,113 +75,113 @@ class PathMoveSystem : public EntitySystem, public EventSubscriber { } void tick(World *pWorld, float deltaTime) override { - pWorld->each( - [&](Entity *ent, ComponentHandle transform, ComponentHandle pathmove) { - if (!pathmove->is_active) return; + pWorld->each([&](Entity *ent, ComponentHandle transform, + ComponentHandle pathmove) { + if (!pathmove->is_active) return; - // Handle change in speed - pathmove->speed += pathmove->speed_addition * deltaTime; - pathmove->speed = glm::clamp(pathmove->speed, 0.0, 10.0); + // Handle change in speed + pathmove->speed += pathmove->speed_addition * deltaTime; + pathmove->speed = glm::clamp(pathmove->speed, 0.0, 10.0); - // Shorthand for the path (we'll use this a lot) - PathMove::Path path = pathmove->path; + // Shorthand for the path (we'll use this a lot) + PathMove::Path path = pathmove->path; - // Add the passed time - float desired_distance = deltaTime * pathmove->speed; // TODO - pathmove->time_passed += desired_distance / path.distances[pathmove->current_point_index]; + // Add the passed time + float desired_distance = deltaTime * pathmove->speed; // TODO + pathmove->time_passed += + desired_distance / path.distances[pathmove->current_point_index]; - // Shorthand for number of points in the path - int num_points = path.points.size(); + // Shorthand for number of points in the path + int num_points = path.points.size(); - if (pathmove->time_passed >= 1.0) { - // If we passed the last target, set the current_point_index to that target - pathmove->time_passed -= 1.0; - pathmove->current_point_index += 1; + if (pathmove->time_passed >= 1.0) { + // If we passed the last target, set the current_point_index to that target + pathmove->time_passed -= 1.0; + pathmove->current_point_index += 1; - // If the point index is greater than the second to last one, reset - // (The point index specifies the point we're coming from, not the one we're moving towards) - if (pathmove->current_point_index >= num_points - 1) { - pathmove->current_point_index = 0; - } - } + // If the point index is greater than the second to last one, reset + // (The point index specifies the point we're coming from, not the one we're moving + // towards) + if (pathmove->current_point_index >= num_points - 1) { + pathmove->current_point_index = 0; + } + } - // The four points which are needed for the spline - // p1 and p2 are always the same (the current origin and the current target), but the rest depends on edge cases - glm::vec3 p0; - glm::vec3 p1 = path.points[pathmove->current_point_index]; - glm::vec3 p2 = path.points[pathmove->current_point_index + 1]; - glm::vec3 p3; + // The four points which are needed for the spline + // p1 and p2 are always the same (the current origin and the current target), but the + // rest depends on edge cases + glm::vec3 p0; + glm::vec3 p1 = path.points[pathmove->current_point_index]; + glm::vec3 p2 = path.points[pathmove->current_point_index + 1]; + glm::vec3 p3; - if (pathmove->current_point_index == num_points - 2) { - // We're moving towards the last point, so the point after that needs to be interpolated. - // We interpolate linearly along the line from this point to the target point. - glm::vec3 interp_direction = p2 - p1; - p3 = p2 + interp_direction * 2.0f; - } else { - // We're fine - use the point after the target for p3 - p3 = path.points[pathmove->current_point_index + 2]; - } + if (pathmove->current_point_index == num_points - 2) { + // We're moving towards the last point, so the point after that needs to be + // interpolated. We interpolate linearly along the line from this point to the + // target point. + glm::vec3 interp_direction = p2 - p1; + p3 = p2 + interp_direction * 2.0f; + } else { + // We're fine - use the point after the target for p3 + p3 = path.points[pathmove->current_point_index + 2]; + } - if (pathmove->current_point_index == 0) { - // We're at the first point, so the point before this needs to be interpolated. - // We interpolate linearly along the line from this to the next point (backwards). - glm::vec3 interp_direction = path.points[pathmove->current_point_index] - path.points[pathmove->current_point_index + 1]; - p0 = path.points[pathmove->current_point_index] + interp_direction; - } else { - // We're fine - use the point before the current point - p0 = path.points[pathmove->current_point_index - 1]; - } + if (pathmove->current_point_index == 0) { + // We're at the first point, so the point before this needs to be interpolated. + // We interpolate linearly along the line from this to the next point (backwards). + glm::vec3 interp_direction = path.points[pathmove->current_point_index] - + path.points[pathmove->current_point_index + 1]; + p0 = path.points[pathmove->current_point_index] + interp_direction; + } else { + // We're fine - use the point before the current point + p0 = path.points[pathmove->current_point_index - 1]; + } - // Calculate the point on the spline - glm::vec3 point = catmul(1.f, - p0, - p1, - p2, - p3, - pathmove->time_passed); - - // Apply - transform->set_origin(point); + // Calculate the point on the spline + glm::vec3 point = catmul(1.f, p0, p1, p2, p3, pathmove->time_passed); - // Rotation - // https://www.3dgep.com/understanding-quaternions/#SQUAD - PathMove::Views views = pathmove->views; + // Apply + transform->set_origin(point); - // Similar procedure as with position to get the relevant values - glm::quat q0; - glm::quat q1 = views.views[pathmove->current_point_index]; - glm::quat q2 = views.views[pathmove->current_point_index + 1]; - glm::quat q3; + // Rotation + // https://www.3dgep.com/understanding-quaternions/#SQUAD + PathMove::Views views = pathmove->views; - if (pathmove->current_point_index == num_points - 2) { - // Interpolate what q3 would be if the change from q1 to q2 continues - q3 = glm::fastMix(q1, q2, 2.0f); - } else { - // We're fine - use the point after the target for p3 - q3 = views.views[pathmove->current_point_index + 2]; - } + // Similar procedure as with position to get the relevant values + glm::quat q0; + glm::quat q1 = views.views[pathmove->current_point_index]; + glm::quat q2 = views.views[pathmove->current_point_index + 1]; + glm::quat q3; - if (pathmove->current_point_index == 0) { - // Interpolate what q0 would be if the same change happened from q0 to q1 as from q1 to q2 - q0 = glm::fastMix(q1, q2, -1.0f); - } else { - // We're fine - use the point before the current point - q0 = views.views[pathmove->current_point_index - 1]; - } + if (pathmove->current_point_index == num_points - 2) { + // Interpolate what q3 would be if the change from q1 to q2 continues + q3 = glm::fastMix(q1, q2, 2.0f); + } else { + // We're fine - use the point after the target for p3 + q3 = views.views[pathmove->current_point_index + 2]; + } - // Interpolate - glm::quat result = glm::squad(q1, q2, glm::intermediate(q0, q1, q2), glm::intermediate(q1, q2, q3), pathmove->time_passed); + if (pathmove->current_point_index == 0) { + // Interpolate what q0 would be if the same change happened from q0 to q1 as from q1 + // to q2 + q0 = glm::fastMix(q1, q2, -1.0f); + } else { + // We're fine - use the point before the current point + q0 = views.views[pathmove->current_point_index - 1]; + } - // Apply - transform->set_rotation_from_quat(result); - }); + // Interpolate + glm::quat result = glm::squad(q1, q2, glm::intermediate(q0, q1, q2), + glm::intermediate(q1, q2, q3), pathmove->time_passed); + + // Apply + transform->set_rotation_from_quat(result); + }); } - void unconfigure(World *pWorld) override { - pWorld->unsubscribeAll(this); - } + void unconfigure(World *pWorld) override { pWorld->unsubscribeAll(this); } -private: + private: World *myWorld; }; diff --git a/ECS/Systems/PositionDebugSystem.h b/ECS/Systems/PositionDebugSystem.h index ee4a8ba..9857f3e 100644 --- a/ECS/Systems/PositionDebugSystem.h +++ b/ECS/Systems/PositionDebugSystem.h @@ -7,22 +7,20 @@ #include -#include "../ECS.h" #include "../Components/Transform.h" +#include "../ECS.h" using namespace ECS; class PositionDebugOutputSystem : public EntitySystem { -public: + public: void tick(World *pWorld, float deltaTime) override { pWorld->each([&](Entity *ent, ComponentHandle transform) { - std::cout << ent->getEntityId() << ": " - << transform->get_origin().x << ", " - << transform->get_origin().y << ", " - << transform->get_origin().z + std::cout << ent->getEntityId() << ": " << transform->get_origin().x << ", " + << transform->get_origin().y << ", " << transform->get_origin().z << std::endl; }); } }; -#endif //ECSGAME_POSITIONDEBUGSYSTEM_H +#endif // ECSGAME_POSITIONDEBUGSYSTEM_H diff --git a/ECS/Systems/RenderSystem.h b/ECS/Systems/RenderSystem.h index 2e42b13..10f3bc6 100644 --- a/ECS/Systems/RenderSystem.h +++ b/ECS/Systems/RenderSystem.h @@ -5,32 +5,28 @@ #ifndef ECSGAME_RENDERSYSTEM_H #define ECSGAME_RENDERSYSTEM_H -#include "../ECS.h" -#include "../Components/Transform.h" -#include "../Components/Mesh.h" -#include "../Components/Camera.h" +#include "../../Rendering/Material.h" #include "../../Rendering/Shader.h" +#include "../Components/Camera.h" +#include "../Components/DirectionalLight.h" +#include "../Components/LODObjMesh.h" +#include "../Components/Mesh.h" #include "../Components/ObjMesh.h" #include "../Components/Texture.h" -#include "../Components/LODObjMesh.h" -#include "../Components/DirectionalLight.h" -#include "../../Rendering/Material.h" +#include "../Components/Transform.h" +#include "../ECS.h" using namespace ECS; // For Debugging unsigned int quadVAO = 0; unsigned int quadVBO; -void renderQuad() -{ - if (quadVAO == 0) - { +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, + -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); @@ -39,9 +35,10 @@ void renderQuad() 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); + 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))); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), + (void *)(3 * sizeof(float))); } glBindVertexArray(quadVAO); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); @@ -49,16 +46,13 @@ void renderQuad() } class RenderSystem : public EntitySystem, public EventSubscriber { -public: + public: struct RenderObject { - RenderObject(const glm::mat4 &matrix, const glm::vec3 &origin, unsigned int textureId, unsigned int normalId, const Mesh &mesh, float distance, const Material &material) - : matrix(matrix), - origin(origin), - texture_id(textureId), - normal_id(normalId), - mesh(mesh), - distance(distance), - material(material) {} + RenderObject(const glm::mat4 &matrix, const glm::vec3 &origin, unsigned int textureId, + unsigned int normalId, const Mesh &mesh, float distance, + const Material &material) + : matrix(matrix), origin(origin), texture_id(textureId), normal_id(normalId), + mesh(mesh), distance(distance), material(material) {} void render(Shader shader) const { glm::mat4 model_matrix = matrix; @@ -95,26 +89,33 @@ public: // Fill the passed vector with render objects of the given world std::vector> getRenderObjects(World *world, glm::vec3 cameraPos) { - std::vector> renderObjects(2); // First normal, then transparent + std::vector> renderObjects(2); // First normal, then transparent - /*pWorld->each([&](Entity *ent, ComponentHandle mesh, ComponentHandle transform) { + /*pWorld->each([&](Entity *ent, ComponentHandle mesh, + ComponentHandle transform) { renderObjects.emplace_back(RenderObject(transform->matrix, 0, mesh.get(), 0)); });*/ - /*// TODO: Is it possible to do get ObjMeshes in the Mesh loop above implicitly via polymorphism? - * // TODO: Commented out because of double rendering - we only want to get objects that explicitly DON'T have a texture here! - pWorld->each([&](Entity *ent, ComponentHandle mesh, ComponentHandle transform) { - // Add the object to the renderObjects to draw if the distance is within the min and max distance of the mesh - float distance = glm::distance(cameraPos, transform->getPosition()); + /*// TODO: Is it possible to do get ObjMeshes in the Mesh loop above implicitly via + polymorphism? + * // TODO: Commented out because of double rendering - we only want to get objects that + explicitly DON'T have a texture here! pWorld->each([&](Entity *ent, + ComponentHandle mesh, ComponentHandle transform) { + // Add the object to the renderObjects to draw if the distance is within the min and max + distance of the mesh float distance = glm::distance(cameraPos, transform->getPosition()); if (distance > mesh->minDistance && distance < mesh->maxDistance) { - renderObjects.emplace_back(RenderObject(transform->matrix, 0, mesh.get(), distance)); + renderObjects.emplace_back(RenderObject(transform->matrix, 0, mesh.get(), + distance)); } });*/ // ObjMesh with textures - world->each([&](Entity *ent, ComponentHandle mesh, ComponentHandle transform, ComponentHandle texture) { - // Add the object to the renderObjects to draw if the distance is within the min and max distance of the mesh + world->each([&](Entity *ent, ComponentHandle mesh, + ComponentHandle transform, + ComponentHandle texture) { + // Add the object to the renderObjects to draw if the distance is within the min and max + // distance of the mesh float distance = glm::distance(cameraPos, transform->get_origin()); if (distance > mesh->minDistance && distance < mesh->maxDistance) { @@ -122,21 +123,29 @@ public: ComponentHandle textureComponent = ent->get(); ComponentHandle materialComponent = ent->get(); - Material material = materialComponent.isValid() ? materialComponent.get() : Material(); + Material material = + materialComponent.isValid() ? materialComponent.get() : Material(); unsigned int textureID = textureComponent.isValid() ? textureComponent->id : 0; - unsigned int normalID = textureComponent.isValid() ? textureComponent->normal_id : 0; + unsigned int normalID = + textureComponent.isValid() ? textureComponent->normal_id : 0; - // Put it into the list of transparent render objects if the texture wants to be rendered transparently + // Put it into the list of transparent render objects if the texture wants to be + // rendered transparently if (textureComponent.isValid() && textureComponent->render_transparent) { - renderObjects[1].emplace_back(RenderObject(transform->matrix, transform->get_origin(), textureID, normalID, mesh.get(), distance, material)); + renderObjects[1].emplace_back( + RenderObject(transform->matrix, transform->get_origin(), textureID, + normalID, mesh.get(), distance, material)); } else { - renderObjects[0].emplace_back(RenderObject(transform->matrix, transform->get_origin(), textureID, normalID, mesh.get(), distance, material)); + renderObjects[0].emplace_back( + RenderObject(transform->matrix, transform->get_origin(), textureID, + normalID, mesh.get(), distance, material)); } } }); // LODObjMesh with Texture - world->each([&](Entity *ent, ComponentHandle lodMesh, ComponentHandle transform) { + world->each([&](Entity *ent, ComponentHandle lodMesh, + ComponentHandle transform) { float distance = glm::distance(cameraPos, transform->get_origin()); for (const auto &mesh : lodMesh->meshes) { @@ -145,31 +154,37 @@ public: ComponentHandle textureComponent = ent->get(); ComponentHandle materialComponent = ent->get(); - Material material = materialComponent.isValid() ? materialComponent.get() : Material(); + Material material = + materialComponent.isValid() ? materialComponent.get() : Material(); unsigned int textureID = textureComponent.isValid() ? textureComponent->id : 0; - unsigned int normalID = textureComponent.isValid() ? textureComponent->normal_id : 0; + unsigned int normalID = + textureComponent.isValid() ? textureComponent->normal_id : 0; - // Put it into the list of transparent render objects if the texture wants to be rendered transparently + // Put it into the list of transparent render objects if the texture wants to be + // rendered transparently if (textureComponent.isValid() && textureComponent->render_transparent) { - renderObjects[1].emplace_back(RenderObject(transform->matrix, transform->get_origin(), textureID, normalID, mesh, distance, material)); + renderObjects[1].emplace_back( + RenderObject(transform->matrix, transform->get_origin(), textureID, + normalID, mesh, distance, material)); } else { - renderObjects[0].emplace_back(RenderObject(transform->matrix, transform->get_origin(), textureID, normalID, mesh, distance, material)); + renderObjects[0].emplace_back( + RenderObject(transform->matrix, transform->get_origin(), textureID, + normalID, mesh, distance, material)); } } } }); // Sort transparent objects - std::sort(renderObjects[1].begin(), renderObjects[1].end(), [](const RenderObject &first, const RenderObject &second) -> bool { - return first.distance > second.distance; - }); + std::sort(renderObjects[1].begin(), renderObjects[1].end(), + [](const RenderObject &first, const RenderObject &second) -> bool { + return first.distance > second.distance; + }); return renderObjects; } - RenderSystem() { - setup(); - } + RenderSystem() { setup(); } void configure(World *pWorld) override { myWorld = pWorld; @@ -196,12 +211,13 @@ public: // Create depth texture glGenTextures(1, &depthMap); glBindTexture(GL_TEXTURE_2D, depthMap); - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, shadow_width, shadow_height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + 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, depthMapFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0); @@ -210,11 +226,12 @@ public: glBindFramebuffer(GL_FRAMEBUFFER, 0); } - void render(World *pWorld, Shader normalShader, Shader shadowShader, Shader debugShader) { - pWorld->each([&](Entity *ent, ComponentHandle camera, ComponentHandle cameraTransform) { + pWorld->each([&](Entity *ent, ComponentHandle camera, + ComponentHandle cameraTransform) { // Get render objects - std::vector> allRenderObjects = getRenderObjects(pWorld, cameraTransform->get_origin()); + std::vector> allRenderObjects = + getRenderObjects(pWorld, cameraTransform->get_origin()); std::vector renderObjects = allRenderObjects[0]; std::vector transparentRenderObjects = allRenderObjects[1]; @@ -222,19 +239,22 @@ public: // Get light direction // TODO: Currently only the last light is used! glm::vec3 lightDirection; - pWorld->each([&](Entity *ent, ComponentHandle light) { - lightDirection = light->direction; - }); + pWorld->each( + [&](Entity *ent, ComponentHandle light) { + lightDirection = light->direction; + }); float near_plane = 1.0f, far_plane = 100.0f; - glm::mat4 lightProjection = glm::ortho(-20.0f, 20.0f, -20.0f, 20.0f, near_plane, far_plane); - glm::mat4 lightView = glm::lookAt(lightDirection * 40.0f, -lightDirection, glm::vec3(0.0, 1.0, 0.0)); + glm::mat4 lightProjection = + glm::ortho(-20.0f, 20.0f, -20.0f, 20.0f, near_plane, far_plane); + glm::mat4 lightView = + glm::lookAt(lightDirection * 40.0f, -lightDirection, glm::vec3(0.0, 1.0, 0.0)); glm::mat4 lightSpaceMatrix = lightProjection * lightView; // Render shadows shadowShader.use(); shadowShader.setMat4("lightSpaceMatrix", lightSpaceMatrix); - + glViewport(0, 0, shadow_width, shadow_height); glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -249,7 +269,7 @@ public: // Render Normal glViewport(0, 0, screen_width, screen_height); - + glClearColor(0.6f, 0.9f, 0.9f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -301,4 +321,4 @@ public: World *myWorld; }; -#endif //ECSGAME_RENDERSYSTEM_H +#endif // ECSGAME_RENDERSYSTEM_H diff --git a/ECS/Systems/SineAnimationSystem.h b/ECS/Systems/SineAnimationSystem.h index 6783df8..5f8c8a5 100644 --- a/ECS/Systems/SineAnimationSystem.h +++ b/ECS/Systems/SineAnimationSystem.h @@ -5,24 +5,27 @@ #ifndef ECSGAME_SINEANIMATIONSYSTEM_H #define ECSGAME_SINEANIMATIONSYSTEM_H -#include "../ECS.h" -#include "../Components/Transform.h" #include "../Components/SineAnimation.h" +#include "../Components/Transform.h" +#include "../ECS.h" using namespace ECS; class SineAnimationSystem : public EntitySystem { -public: + public: void tick(World *pWorld, float deltaTime) override { passedTime += deltaTime; - pWorld->each([&](Entity *ent, ComponentHandle transform, ComponentHandle anim) { - transform->translate(anim->maxDistance * glm::sin(passedTime * anim->speedScale) * deltaTime); + pWorld->each([&](Entity *ent, + ComponentHandle transform, + ComponentHandle anim) { + transform->translate(anim->maxDistance * glm::sin(passedTime * anim->speedScale) * + deltaTime); }); } -private: + private: float passedTime = 0.0; }; -#endif //ECSGAME_SINEANIMATIONSYSTEM_H +#endif // ECSGAME_SINEANIMATIONSYSTEM_H diff --git a/Rendering/Material.h b/Rendering/Material.h index 17eb511..94b6340 100644 --- a/Rendering/Material.h +++ b/Rendering/Material.h @@ -17,4 +17,4 @@ struct Material { float normal_scale = 3.0; }; -#endif //ECSGAME_MATERIAL_H +#endif // ECSGAME_MATERIAL_H diff --git a/Rendering/Shader.cpp b/Rendering/Shader.cpp index 0ec7b77..b29368b 100644 --- a/Rendering/Shader.cpp +++ b/Rendering/Shader.cpp @@ -5,11 +5,11 @@ #include "Shader.h" #include -#include #include -#include -#include #include +#include +#include +#include Shader::Shader(const char *vertexPath, const char *fragmentPath) { // 1. retrieve the vertex/fragment source code from filePath @@ -36,8 +36,7 @@ Shader::Shader(const char *vertexPath, const char *fragmentPath) { // convert stream into string vertexCode = vShaderStream.str(); fragmentCode = fShaderStream.str(); - } - catch (std::ifstream::failure &e) { + } catch (std::ifstream::failure &e) { std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl; } @@ -69,7 +68,6 @@ Shader::Shader(const char *vertexPath, const char *fragmentPath) { // delete the shaders as they're linked into our program now and no longer necessary glDeleteShader(vertex); glDeleteShader(fragment); - } void Shader::use() { @@ -77,7 +75,7 @@ void Shader::use() { } void Shader::setBool(const std::string &name, bool value) const { - glUniform1i(glGetUniformLocation(ID, name.c_str()), (int) value); + glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value); } void Shader::setInt(const std::string &name, int value) const { @@ -92,7 +90,6 @@ void Shader::setMat4(const std::string &name, glm::mat4 mat) const { glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, glm::value_ptr(mat)); } - void Shader::setVec3(const std::string &name, glm::vec3 vec) const { glUniform3f(glGetUniformLocation(ID, name.c_str()), vec.x, vec.y, vec.z); } @@ -104,15 +101,17 @@ void Shader::checkCompileErrors(unsigned int shader, const std::string &type) { glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(shader, 1024, nullptr, infoLog); - std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog - << "\n -- --------------------------------------------------- -- " << std::endl; + std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" + << infoLog << "\n -- --------------------------------------------------- -- " + << std::endl; } } else { glGetProgramiv(shader, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shader, 1024, nullptr, infoLog); - std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog - << "\n -- --------------------------------------------------- -- " << std::endl; + std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" + << infoLog << "\n -- --------------------------------------------------- -- " + << std::endl; } } } diff --git a/Rendering/Shader.h b/Rendering/Shader.h index f0defee..f88c22a 100644 --- a/Rendering/Shader.h +++ b/Rendering/Shader.h @@ -5,16 +5,17 @@ #ifndef ECSGAME_SHADER_H #define ECSGAME_SHADER_H -#include #include +#include class Shader { -public: + public: /// Program ID unsigned int ID; /// Read and build the shader from the files at vertexPath and fragmentPath. - /// Note that an OpenGL context has to be initialized before calling this! Otherwise a SIGSEGV will be thrown. + /// Note that an OpenGL context has to be initialized before calling this! Otherwise a SIGSEGV + /// will be thrown. Shader(const char *vertexPath, const char *fragmentPath); /// Activate the shader - usually called before rendering. @@ -35,9 +36,8 @@ public: /// Set a uniform vec3 in the shader void setVec3(const std::string &name, glm::vec3 vec) const; -private: + private: static void checkCompileErrors(unsigned int shader, const std::string &type); }; - -#endif //ECSGAME_SHADER_H +#endif // ECSGAME_SHADER_H diff --git a/Util/OBJ_Loader.h b/Util/OBJ_Loader.h index 0b7d010..41accaa 100644 --- a/Util/OBJ_Loader.h +++ b/Util/OBJ_Loader.h @@ -24,1144 +24,954 @@ // // 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& _Vertices, std::vector& _Indices) - { - Vertices = _Vertices; - Indices = _Indices; - } - // Mesh Name - std::string MeshName; - // Vertex List - std::vector Vertices; - // Index List - std::vector 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: 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 &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 - inline const T & getElement(const std::vector &elements, std::string &index) - { - int idx = std::stoi(index); - if (idx < 0) - idx = int(elements.size()) + idx; - else - idx--; - return elements[idx]; - } - } - - // 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 Positions; - std::vector TCoords; - std::vector Normals; - - std::vector Vertices; - std::vector Indices; - - std::vector 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 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 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 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 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 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 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 LoadedMeshes; - // Loaded Vertex Objects - std::vector LoadedVertices; - // Loaded Index Positions - std::vector LoadedIndices; - // Loaded Material Objects - std::vector LoadedMaterials; - - private: - // Generate vertices from a list of positions, - // tcoords, normals and a face line - void GenVerticesFromRawOBJ(std::vector& oVerts, - const std::vector& iPositions, - const std::vector& iTCoords, - const std::vector& iNormals, - std::string icurline) - { - std::vector 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& oIndices, - const std::vector& 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 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 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 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 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 { +// 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 &_Vertices, std::vector &_Indices) { + Vertices = _Vertices; + Indices = _Indices; + } + // Mesh Name + std::string MeshName; + // Vertex List + std::vector Vertices; + // Index List + std::vector 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 &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 inline const T &getElement(const std::vector &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 Positions; + std::vector TCoords; + std::vector Normals; + + std::vector Vertices; + std::vector Indices; + + std::vector 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 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 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 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 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 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 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 LoadedMeshes; + // Loaded Vertex Objects + std::vector LoadedVertices; + // Loaded Index Positions + std::vector LoadedIndices; + // Loaded Material Objects + std::vector LoadedMaterials; + + private: + // Generate vertices from a list of positions, + // tcoords, normals and a face line + void GenVerticesFromRawOBJ(std::vector &oVerts, const std::vector &iPositions, + const std::vector &iTCoords, + const std::vector &iNormals, std::string icurline) { + std::vector 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 &oIndices, + const std::vector &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 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 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 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 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 diff --git a/main.cpp b/main.cpp index 134eed7..db2be66 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,3 @@ -#include #include #include