diff --git a/CMake/GetEnTT.cmake b/CMake/GetEnTT.cmake new file mode 100644 index 0000000..0cdeaea --- /dev/null +++ b/CMake/GetEnTT.cmake @@ -0,0 +1,10 @@ +# Get the entt header libraries. +include(FetchContent) +FetchContent_Declare( + entt + GIT_REPOSITORY https://github.com/skypjack/entt.git + GIT_TAG v3.10.3 +) +FetchContent_MakeAvailable(entt) +message("Downloaded entt library to: ${entt_SOURCE_DIR}") +set(ENTT_INCLUDE_DIR ${entt_SOURCE_DIR}/src) \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 53cdba1..d30ac6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ project(SimpleEngine) include(cmrc/CMakeRC.cmake) include(CMake/GetStbLibraries.cmake) +include(CMake/GetEnTT.cmake) # Add some CMake options: option(SIMPLE_ENGINE_BUILD_EXAMPLES "Build example projects" ON) @@ -47,11 +48,11 @@ target_link_libraries(simpleengine PUBLIC ${OPENGL_LIBRARIES}) target_link_libraries(simpleengine PUBLIC assimp) target_link_libraries(simpleengine PRIVATE simpleengine_resources) -target_include_directories(simpleengine PUBLIC ${STB_INCLUDE_DIR}) - # Include some dependencies' include directories target_include_directories(simpleengine PUBLIC ${OPENGL_INCLUDE_DIR}) target_include_directories(simpleengine PUBLIC ${GLM_INCLUDE_DIRS}) +target_include_directories(simpleengine PUBLIC ${STB_INCLUDE_DIR}) +target_include_directories(simpleengine PUBLIC ${ENTT_INCLUDE_DIR}) # Add examples as a target if the user has them enabled if (SIMPLE_ENGINE_BUILD_EXAMPLES) diff --git a/examples/dev_testing/resources/.gitignore b/examples/dev_testing/resources/.gitignore index e687263..cad6e74 100644 --- a/examples/dev_testing/resources/.gitignore +++ b/examples/dev_testing/resources/.gitignore @@ -1,5 +1,2 @@ -*.blend -*.obj -*.png -*.bin -*.gltf \ No newline at end of file +* +!shaders \ No newline at end of file diff --git a/examples/dev_testing/src/main.cpp b/examples/dev_testing/src/main.cpp index 632a488..269f46b 100644 --- a/examples/dev_testing/src/main.cpp +++ b/examples/dev_testing/src/main.cpp @@ -1,8 +1,9 @@ #include "simpleengine/camera.h" #include "simpleengine/ecs/component/mesh_component.h" #include +#include "simpleengine/ecs/component/transform_component.h" +#include #include "simpleengine/ecs/entity.h" -#include "simpleengine/entity_manager.h" #include "simpleengine/gfx/light.h" #include "simpleengine/gfx/material.h" #include "simpleengine/gfx/mesh.h" @@ -10,7 +11,6 @@ #include "simpleengine/gfx/renderer.h" #include "simpleengine/gfx/texture.h" #include "simpleengine/vector.h" -#include #include #include #include @@ -19,8 +19,7 @@ #include #include #include - -//#include +#include #include #include @@ -166,29 +165,45 @@ int main(int argc, char *argv[]) { 5, 6, 12, 12, 6, 13 }; - std::unordered_map> textures; - textures.emplace(white_texture.type, std::vector{ white_texture }); + std::unordered_map>> textures; + textures.emplace(white_texture.type, std::vector>{ std::make_shared(white_texture) }); se::gfx::Material white_material(textures, 1.f, 0.f, 0.f, 0.f, 0.f); + // Create a renderer + auto renderer = std::make_shared(game.get_window(), core_shader); + game.add_renderable(renderer); + + // Create a Scene and give it the renderer + auto scene = std::make_shared(renderer); + game.add_event(scene); + + // Create an Entity in the Scene and add components to it. + se::ecs::Entity entity = scene->create_entity(); + //entity.add_component("examples/dev_testing/resources/dragon.obj"); + //entity.add_component("examples/dev_testing/resources/stall.obj"); + + // Backpack model required vertically flipped texture coords. + auto& model_comp = entity.add_component("examples/dev_testing/resources/backpack/backpack.obj"); + model_comp.model.vertically_flip_tex_coords(); + + //entity.add_component("examples/dev_testing/resources/scientist/scientist.fbx"); + //entity.add_component("examples/dev_testing/resources/paradigm/paradigm.fbx"); + //entity.add_component(); + auto& transform_comp = entity.add_component(); + transform_comp.translate(15.f, -8.f, 0.f); + /* transform_comp.scale(0.1f); + transform_comp.rotate_z(-90.f);*/ + transform_comp.rotate_x(-90.f); + + // Create the entity and add the model component to it. /* auto entity = std::make_shared(); entity->add_component(cube_vertices, cube_indicies, white_material, true); entity->translate(3.5f, 0.f, 0.f); */ - auto entity = std::make_shared(); + /* auto entity = std::make_shared(); entity->add_component("examples/dev_testing/resources/dragon.obj"); - entity->translate(12.f, -4.f, 0.f); - - // Create a renderer and submit the entity into it. - auto renderer = std::make_shared(game.get_window(), core_shader); - renderer->enable_debug(); - renderer->submit_entity(entity); - game.add_renderable(renderer); - - // Create an EntityManager, and submit the entity into it. - auto ecs_manager = std::make_shared(); - ecs_manager->submit_entity(entity); - game.add_event(ecs_manager); + entity->translate(12.f, -4.f, 0.f); */ auto camera = std::make_shared(game.get_window(), core_shader, 70, glm::vec3(0, 0, 0)); game.add_event(camera); diff --git a/include/simpleengine/ecs/component/component.h b/include/simpleengine/ecs/component/component.h deleted file mode 100644 index d132f27..0000000 --- a/include/simpleengine/ecs/component/component.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "../../gfx/mesh.h" -#include "../../event/event.h" - -#include -#include - -namespace simpleengine { - /** - * @brief A Model is a object that will be shown on the screen by a renderer. - * - */ - class Component : public simpleengine::Event { - private: - static uint32_t incrementing_handle; - uint32_t handle; - public: - Component() { - handle = incrementing_handle++; - } - - uint32_t get_handle() { - return handle; - } - - virtual void update(const float& delta_time) override { - std::cout << "Component update" << std::endl; - } - }; -} \ No newline at end of file diff --git a/include/simpleengine/ecs/component/mesh_component.h b/include/simpleengine/ecs/component/mesh_component.h index 3daff3e..6e2bf15 100644 --- a/include/simpleengine/ecs/component/mesh_component.h +++ b/include/simpleengine/ecs/component/mesh_component.h @@ -1,6 +1,5 @@ #pragma once -#include "component.h" #include "../../gfx/mesh.h" #include "../../gfx/material.h" @@ -9,37 +8,32 @@ namespace simpleengine { /** - * @brief A Model is a object that will be shown on the screen by a renderer. + * @brief A component that contains a Mesh that will be rendered. * */ - class MeshComponent : public simpleengine::Component { + class MeshComponent { public: - gfx::Mesh model; - //gfx::Material material; + gfx::Mesh mesh; - MeshComponent(gfx::Mesh model) : model(model) { + MeshComponent(gfx::Mesh mesh) : mesh(mesh) { } MeshComponent(std::vector vertices, std::vector indicies, gfx::Material material, - bool calculate_normals = false): model(vertices, indicies, material) { + bool calculate_normals = false): mesh(vertices, indicies, material) { if (calculate_normals) { - model.calculate_normals(); + mesh.calculate_normals(); } } MeshComponent(std::vector vertices, std::vector indicies = std::vector(), std::optional material = std::nullopt, bool calculate_normals = false) : - model(vertices, indicies, material) { + mesh(vertices, indicies, material) { if (calculate_normals) { - model.calculate_normals(); + mesh.calculate_normals(); } } - - virtual void update(const float& delta_time) override { - - } }; } \ No newline at end of file diff --git a/include/simpleengine/ecs/component/model_component.h b/include/simpleengine/ecs/component/model_component.h index 9d6dfda..cac09de 100644 --- a/include/simpleengine/ecs/component/model_component.h +++ b/include/simpleengine/ecs/component/model_component.h @@ -1,6 +1,5 @@ #pragma once -#include "component.h" #include "../../gfx/model.h" #include @@ -8,10 +7,10 @@ namespace simpleengine { /** - * @brief A Model is a object that will be shown on the screen by a renderer. + * @brief A component that contains a Model that will be rendered. * */ - class ModelComponent : public simpleengine::Component { + class ModelComponent { public: gfx::Model model; @@ -19,12 +18,9 @@ namespace simpleengine { } - ModelComponent(std::string model_file_path) : model(model_file_path) { + ModelComponent(std::string model_file_path, int model_processing_flags = gfx::ModelProcessingFlags::MdlProcFlag_NONE, + int assimp_flags = 0) : model(model_file_path, model_processing_flags, assimp_flags) { } - - virtual void update(const float& delta_time) override { - - } }; } \ No newline at end of file diff --git a/include/simpleengine/ecs/component/rotating_component.h b/include/simpleengine/ecs/component/rotating_component.h new file mode 100644 index 0000000..ab33827 --- /dev/null +++ b/include/simpleengine/ecs/component/rotating_component.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace simpleengine { + /** + * @brief A component that will rotate the transform every frame. + * + */ + class RotatingComponent { + public: + float rate; + glm::vec3 rotation_axis; + + RotatingComponent() : rate(10.f), rotation_axis(glm::vec3(0, 1, 0)) { + + } + + RotatingComponent(float rate) : rate(rate), rotation_axis(glm::vec3(0, 1, 0)) { + + } + + RotatingComponent(glm::vec3 rotation_axis) : rotation_axis(rotation_axis) { + + } + + RotatingComponent(float rate, glm::vec3 rotation_axis) : rate(rate), rotation_axis(rotation_axis) { + + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/ecs/component/transform_component.h b/include/simpleengine/ecs/component/transform_component.h new file mode 100644 index 0000000..f0fa948 --- /dev/null +++ b/include/simpleengine/ecs/component/transform_component.h @@ -0,0 +1,99 @@ +#pragma once + +#include +#include + +namespace simpleengine { + /** + * @brief A component that contains a Mesh that will be rendered. + * + */ + class TransformComponent { + public: + glm::mat4 transform_matrix; + + TransformComponent() : transform_matrix(glm::mat4(1.f)) { + + } + + TransformComponent(glm::mat4 transform_matrix) : transform_matrix(transform_matrix) { + + } + + friend TransformComponent operator+(TransformComponent lhs, const TransformComponent& rhs) { + lhs.transform_matrix += rhs.transform_matrix; + return lhs; + } + + friend TransformComponent operator-(TransformComponent lhs, const TransformComponent& rhs) { + lhs.transform_matrix -= rhs.transform_matrix; + return lhs; + } + + friend TransformComponent operator*(TransformComponent lhs, const TransformComponent& rhs) { + lhs.transform_matrix *= rhs.transform_matrix; + return lhs; + } + + friend TransformComponent operator/(TransformComponent lhs, const TransformComponent& rhs) { + lhs.transform_matrix /= rhs.transform_matrix; + return lhs; + } + + virtual void combine_transform(const glm::mat4& transform_matrix) { + this->transform_matrix *= transform_matrix; + } + + virtual void combine_transform(const TransformComponent& transformable) { + transform_matrix = transformable.transform_matrix; + } + + virtual void translate(float x, float y, float z) { + transform_matrix = glm::translate(transform_matrix, glm::vec3(x, y, z)); + } + + /* virtual void translate(const glm::vec3& vec) { + transform_matrix = glm::translate(transform_matrix, vec); + } */ + + virtual glm::mat4 rotation_matrix(float degrees, glm::vec3 rotation_axis) const { + return glm::rotate(transform_matrix, glm::radians(degrees), rotation_axis); + } + + virtual glm::mat4 rotation_x_matrix(float degrees) const { + return rotation_matrix(degrees, glm::vec3(1, 0, 0)); + } + + virtual glm::mat4 rotation_y_matrix(float degrees) const { + return rotation_matrix(degrees, glm::vec3(0, 1, 0)); + } + + virtual glm::mat4 rotation_z_matrix(float degrees) const { + return rotation_matrix(degrees, glm::vec3(0, 0, 1)); + } + + virtual void rotate(float degrees, glm::vec3 rotation_axis) { + transform_matrix = rotation_matrix(degrees, rotation_axis); + } + + virtual void rotate_x(float degrees) { + transform_matrix = rotation_x_matrix(degrees); + } + + virtual void rotate_y(float degrees) { + transform_matrix = rotation_y_matrix(degrees); + } + + virtual void rotate_z(float degrees) { + transform_matrix = rotation_z_matrix(degrees); + } + + virtual void scale(glm::vec3 scalar_vec) { + transform_matrix = glm::scale(transform_matrix, scalar_vec); + } + + virtual void scale(float scalar) { + transform_matrix = glm::scale(transform_matrix, glm::vec3(scalar, scalar, scalar)); + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/ecs/entity.h b/include/simpleengine/ecs/entity.h index e1e5bf9..8cc2675 100644 --- a/include/simpleengine/ecs/entity.h +++ b/include/simpleengine/ecs/entity.h @@ -1,116 +1,266 @@ #pragma once -#include "component/component.h" -#include "../transformable.h" -#include "../util.h" +#include "entt/entity/fwd.hpp" +#include +#include +#include -#include -#include -#include -#include -#include -#include -#include - -namespace simpleengine { - /** - * @brief A Model is a object that will be shown on the screen by a renderer. - * - */ - class Entity : public simpleengine::Event, public simpleengine::Transformable { - // TODO: Don't extend from Event, create own destroy function +namespace simpleengine::ecs { + class Entity { private: - static uint32_t incrementing_handle; - uint32_t handle; + entt::registry& registry; + entt::entity inner; public: - std::vector> components; + /* Entity(entt::entity entity) : inner(entity) { - Entity() : components({}) { - handle = incrementing_handle++; - } - - /* Entity() : components({}) { - std::random_device rd; // obtain a random number from hardware - std::mt19937 gen(rd()); // seed the generator - std::uniform_int_distribution distr(1, std::numeric_limits::max()); - - uint16_t num = distr(gen); - uint32_t pid = simpleengine::util::get_current_pid(); - - handle |= num; - handle |= (pid << 16); - - std::string binary = std::bitset<16>(num).to_string(); - std::cout << "Entity handle: " << binary << std::endl; - } */ - Entity(std::vector> components) : Entity() { - this->components = components; + Entity(entt::registry& registry, entt::entity entity) : registry(registry), inner(entity) { + } - uint32_t get_handle() { - return handle; + /** + * @brief Checks if an identifier refers to a valid entity. + * + * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a1795b1221d728f806319a685930f520d + * + * @return true if the identifier is valid + * @return false otherwise + */ + bool is_valid() const { + return registry.valid(inner); } - virtual void update(const float& delta_time) override { - for (auto& component : components) { - component->update(delta_time); - } - - rotate_y(delta_time * 10); // TODO: Remove + /** + * @brief Returns the actual version for an identifier. + * + * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a199babc787d6baa6f7ccce761228a5f6 + * + * @return entt::registry::version_type The version for the given identifier if valid, the tombstone version otherwise. + */ + entt::registry::version_type current_version() const { + return registry.current(inner); } - template - bool has_component() const { - for (const auto& comp : components) { - if (std::dynamic_pointer_cast(comp)) { - return true; - } - } - - return false; + /** + * @brief Releases an identifier. + * + * The version is updated and the identifier can be recycled at any time. + * + * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a3d9cb2368384b0952cb54848e777359e + * + * @return entt::registry::version_type The version of the recycled entity. + */ + entt::registry::version_type release() { + return registry.release(inner); } - template - std::shared_ptr get_component() const { - for (const auto& comp : components) { - if (std::shared_ptr dyn_comp = std::dynamic_pointer_cast(comp); dyn_comp) { - return dyn_comp; - } - } - - return nullptr; + /** + * @brief Releases an identifier. + * + * The suggested version or the valid version closest to the suggested one is used instead of the implicitly generated version. + * + * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#af9c919867fc93a7e1a2d0762ac3a9877 + * + * @param version A desired version upon destruction. + * @return entt::registry::version_type The version actually assigned to the entity. + */ + entt::registry::version_type release(entt::registry::version_type version) { + return registry.release(inner, version); } - template - void add_component(std::shared_ptr component) { - static_assert(std::is_base_of_v, "Component class must derive from simpleengine::Component"); - - // Only allow one type of the same component - assert(!has_component()); // TODO: Don't assert, give an error - components.push_back(component); + /** + * @brief Destroys an entity and releases its identifier. + * + * @warning Adding or removing components to an entity that is being destroyed can result in undefined behavior. Attempting to use an invalid entity results in undefined behavior. + * + * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a7b2c0368d508a6af2d094a9fc592a4a0 + * + * @return entt::registry::version_type The version of the recycled entity. + */ + entt::registry::version_type destroy() { + return registry.destroy(inner); } - template - void add_component(T component) { - static_assert(std::is_base_of_v, "Component class must derive from simpleengine::Component"); - - // Only allow one type of the same component - assert(!has_component()); // TODO: Don't assert, give an error - components.push_back(std::make_shared(component)); + /** + * @brief Destroys an entity and releases its identifier. + * + * The suggested version or the valid version closest to the suggested one is used instead of the implicitly generated version. + * + * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#adc175a0d3bcf83a133c63890c674ceb3 + * + * @param version A desired version upon destruction. + * @return entt::registry::version_type The version actually assigned to the entity. + */ + entt::registry::version_type destroy(entt::registry::version_type version) { + return registry.destroy(inner, version); } - template - std::shared_ptr add_component(Args&&... args) { - static_assert(std::is_base_of_v, "Component class must derive from simpleengine::Component"); + /** + * @brief Assigns the given component to an entity. + * + * The component must have a proper constructor or be of aggregate type. + * + * @warning Attempting to use an invalid entity or to assign a component to an entity that already owns it results in undefined behavior. + * + * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a4c9c0532972adc7a930a50888a97efdf + * + * @tparam Component Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * + * @param args Parameters to use to initialize the component. + * + * @return decltype(auto) A reference to the newly created component. + */ + template + decltype(auto) add_component(Args&&... args) { + return registry.emplace(inner, std::forward(args)...); + } - // Only allow one type of the same component - assert(!has_component()); // TODO: Don't assert, give an error - auto comp = std::make_shared(std::forward(args)...); - components.push_back(comp); + /** + * @brief Assigns or replaces the given component for an entity. + * + * The component must have a proper constructor or be of aggregate type. + * + * @warning Attempting to use an invalid entity or to assign a component to an entity that already owns it results in undefined behavior. + * + * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a3bbaebbb9365eef262453e42a8f7dc4b + * + * @tparam Component Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * + * @param args Parameters to use to initialize the component. + * + * @return decltype(auto) A reference to the newly created component. + */ + template + decltype(auto) add_or_replace_component(Args&&... args) { + return registry.emplace_or_replace(inner, std::forward(args)...); + } - return comp; + /** + * @brief Replaces the given component for an entity. + * + * The component must have a proper constructor or be of aggregate type. + * + * @warning Attempting to use an invalid entity or to replace a component of an entity that doesn't own it results in undefined behavior. + * + * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a0501ff5a96a3421f3eec427bf5d62380 + * + * @tparam Component Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * + * @param args Parameters to use to initialize the component. + * + * @return decltype(auto) A reference to the component being replaced. + */ + template + decltype(auto) replace_component(Args&&... args) { + return registry.replace(inner, std::forward(args)...); + } + + /** + * @brief Patches the given component for an entity. + * + * The signature of the function should be equivalent to the following: + * \code{.cpp} + * void(Component &); + * \endcode + * + * @note Empty types aren't explicitly instantiated and therefore they are never returned. However, this function can be used to trigger an update signal for them. + * + * @warning Attempting to use an invalid entity or to patch a component of an entity that doesn't own it results in undefined behavior. + * + * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a22454253689ff77e41326f849bfea976 + * + * @tparam Component Type of component to patch. + * @tparam Func Types of the function objects to invoke. + * + * @param func Valid function objects. + * + * @return decltype(auto) A reference to the patched component. + */ + template + decltype(auto) patch(Func&&... func) { + return registry.patch(inner, std::forward(func)...); + } + + /** + * @brief Removes the given components from an entity. + * + * @warning Attempting to use an invalid entity results in undefined behavior. + * + * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#aa66ca71c5eb6c2e1b3bde6ff86a268c7 + * + * @tparam Component Type of component to remove. + * @tparam Other Other types of components to remove. + * + * @return size_t The number of components actually removed. + */ + template + size_t remove_components() { + return registry.remove(inner); + } + + /** + * @brief Erases the given components from an entity. + * + * @warning Attempting to use an invalid entity or to erase a component from an entity that doesn't own it results in undefined behavior. + * + * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#ad7a51ade7ff181d7aa0b35915d0a4f2b + * + * @tparam Component Types of components to erase. + * @tparam Other Other types of components to erase. + */ + template + void erase() { + return registry.remove(inner); + } + + /** + * @brief Checks if an entity has all the given components. + * + * @warning Attempting to use an invalid entity results in undefined behavior. + * + * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a40072eca607846f43f47d0e3f11dd196 + * + * @tparam Component Components for which to perform the check. + * + * @return true if the entity has all the components + * @return false otherwise + */ + template + bool has_all_of() const { + return registry.all_of(inner); + } + + /** + * @brief Checks if an entity has at least one of the given components. + * + * @warning Attempting to use an invalid entity results in undefined behavior. + * + * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a08f819276fd2d8b2b2b6d01357f61a42 + * + * @tparam Component Components for which to perform the check. + * + * @return true if the entity has at least one of the given components + * @return false otherwise + */ + template + bool has_any_of() const { + return registry.any_of(inner); + } + + /** + * @brief Checks if an entity has components assigned. + * + * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a10f5e61d4cabab9e9c1e87edc30de551 + * + * @return true if the entity has no components assigned + * @return false otherwise + */ + bool has_any_components() const { + return registry.orphan(inner); } }; } \ No newline at end of file diff --git a/include/simpleengine/entity_manager.h b/include/simpleengine/entity_manager.h deleted file mode 100644 index de7fe2d..0000000 --- a/include/simpleengine/entity_manager.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "ecs/entity.h" -#include "event/event.h" - -#include -#include -#include - -namespace simpleengine { - class EntityManager : public simpleengine::Event { - public: - std::unordered_map> entities; - - EntityManager(); - - virtual void submit_entity(std::shared_ptr entity); - virtual bool withdraw_entity(std::shared_ptr entity); - - virtual void initialize(); - virtual void destroy() override; - - virtual void update(const float& delta_time) override; - }; -} \ No newline at end of file diff --git a/include/simpleengine/gfx/material.h b/include/simpleengine/gfx/material.h index 8dd3817..c024e45 100644 --- a/include/simpleengine/gfx/material.h +++ b/include/simpleengine/gfx/material.h @@ -3,12 +3,14 @@ #include "texture.h" #include + #include +#include namespace simpleengine::gfx { class Material { public: - std::unordered_map> textures; + std::unordered_map>> textures; float ambient_scalar; float diffuse_scalar; @@ -16,7 +18,7 @@ namespace simpleengine::gfx { float shine; float reflectivity; - Material(std::unordered_map> textures, float shine = 1.f, float reflectivity = 0.f, float specular_scalar = 0.f, float ambient_scalar = 0.f, float diffuse_scalar = 0.f) : + Material(std::unordered_map>> textures, float shine = 1.f, float reflectivity = 0.f, float specular_scalar = 0.f, float ambient_scalar = 0.f, float diffuse_scalar = 0.f) : textures(textures), ambient_scalar(ambient_scalar), diffuse_scalar(diffuse_scalar), specular_scalar(specular_scalar), shine(shine), reflectivity(reflectivity) { diff --git a/include/simpleengine/gfx/mesh.h b/include/simpleengine/gfx/mesh.h index 45a82ba..e696053 100644 --- a/include/simpleengine/gfx/mesh.h +++ b/include/simpleengine/gfx/mesh.h @@ -23,6 +23,7 @@ namespace simpleengine::gfx { std::vector vertices; std::vector indicies; + bool are_buffers_created = false; gfx::VBO ebo; gfx::VBO vbo; gfx::VAO vao; diff --git a/include/simpleengine/gfx/model.h b/include/simpleengine/gfx/model.h index 13535ca..a49773d 100644 --- a/include/simpleengine/gfx/model.h +++ b/include/simpleengine/gfx/model.h @@ -10,6 +10,12 @@ //#include namespace simpleengine::gfx { + enum ModelProcessingFlags : uint8_t { + MdlProcFlag_NONE = 0b00000000, + MdlProcFlag_FLIP_TEX_COORDS_VERTICALLY = 0b00000001, + MdlProcFlag_FLIP_TEX_COORDS_HORIZONTALLY = 0b00000010, + }; + /** * @brief A Model is a group of Meshes read from the 3D model file. * @@ -20,14 +26,25 @@ namespace simpleengine::gfx { class Model : public simpleengine::Transformable { protected: std::string model_directory; // May be needed + int additional_assimp_flags; + int model_processing_flags; public: std::vector meshes; - Model(std::string file_path); + Model(std::string file_path, int model_processing_flags = ModelProcessingFlags::MdlProcFlag_NONE, int assimp_flags = 0); void load_model(std::string path); - void process_node(aiNode* node, const aiScene* scene); - gfx::Mesh process_mesh(aiMesh* mesh, const aiScene* scene); - std::vector load_material_textures(aiMaterial* material, aiTextureType type); + void process_node(std::unordered_map>>& processed_textures, aiNode* node, const aiScene* scene); + gfx::Mesh process_mesh(std::unordered_map>>& processed_textures, aiMesh* mesh, const aiScene* scene); + + std::unordered_map> load_all_textures(aiMaterial* material); + std::vector> load_material_texture(std::unordered_map>>& processed_textures, aiMaterial* material, aiTextureType type); + + protected: + void post_process(); + public: + + void vertically_flip_tex_coords(); + void horizontally_flip_tex_coords(); }; } \ No newline at end of file diff --git a/include/simpleengine/gfx/renderer.h b/include/simpleengine/gfx/renderer.h index 8ee23d7..3cb5cce 100644 --- a/include/simpleengine/gfx/renderer.h +++ b/include/simpleengine/gfx/renderer.h @@ -1,45 +1,29 @@ #pragma once -#include "../ecs/entity.h" -#include "material.h" -#include "texture.h" #include "shader.h" -//#include "renderable.h" -#include "mesh.h" -#include "model.h" +#include "simpleengine/gfx/mesh.h" #include #include +#include namespace simpleengine::gfx { + class RenderingJob { + public: + gfx::Mesh& rendering_mesh; + glm::mat4 transform_mat; + + RenderingJob(gfx::Mesh& mesh, glm::mat4 position) : rendering_mesh(mesh), transform_mat(position) { + + } + }; + class Renderer : public simpleengine::Renderable { private: GLFWwindow* window; public: - class RenderingModel { - public: - std::shared_ptr entity; - std::unordered_map component_models; - - RenderingModel(std::shared_ptr entity) : entity(entity) { - - } - - /** - * @brief Create and delete buffers for new and old components in entity. - * - */ - void update_buffers(); - - /** - * @brief Destroy the buffers - * - */ - void destroy_buffers(); - }; - - std::unordered_map rendering_models; + std::queue rendering_queue; gfx::Shader shader; Renderer(GLFWwindow* window, gfx::Shader shader); @@ -47,8 +31,8 @@ namespace simpleengine::gfx { void enable_debug(); - virtual void submit_entity(std::shared_ptr entity); - virtual bool withdraw_entity(std::shared_ptr entity); + virtual void queue_job(RenderingJob job); + virtual void create_job_buffers(RenderingJob& job); virtual void initialize(); virtual void destroy() override; diff --git a/include/simpleengine/gfx/texture.h b/include/simpleengine/gfx/texture.h index b7a8289..302f14b 100644 --- a/include/simpleengine/gfx/texture.h +++ b/include/simpleengine/gfx/texture.h @@ -1,5 +1,6 @@ #pragma once +#include #ifdef __linux__ #include #include @@ -17,6 +18,13 @@ #include namespace simpleengine::gfx { + enum TextureFlags : uint8_t { + TexFlags_IMG_2D = 0b00000001, + TexFlags_FLIP_VERTICALLY = 0b00000010, + TexFlags_FLIP_HORIZONTALLY = 0b00000100, + TexFlags_MIPMAP = 0b00001000, + }; + class Texture { private: unsigned char* img_data; // TODO Free this if its not used anymore @@ -30,6 +38,7 @@ namespace simpleengine::gfx { int width; int channels; aiTextureType type; + std::string path; /** * @brief Construct a new Texture object from a path. @@ -38,7 +47,8 @@ namespace simpleengine::gfx { * @param img_2d Whether or not the texture is 2D. * @param mipmap Whether or not to generate mipmaps for this texture. */ - Texture(const char* path, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, bool img_2d = true, bool mipmap = true); + Texture(const char* path, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = TextureFlags::TexFlags_IMG_2D | + TextureFlags::TexFlags_MIPMAP); /** * @brief Construct a new Texture object from the loaded file buffer. @@ -48,7 +58,8 @@ namespace simpleengine::gfx { * @param img_2d Whether or not the texture is 2D. * @param mipmap Whether or not to generate mipmaps for this texture. */ - Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, bool img_2d = true, bool mipmap = true); + Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = TextureFlags::TexFlags_IMG_2D | + TextureFlags::TexFlags_MIPMAP); /** * @brief Construct a new Texture object from the loaded file buffer. @@ -57,7 +68,8 @@ namespace simpleengine::gfx { * @param img_2d Whether or not the texture is 2D. * @param mipmap Whether or not to generate mipmaps for this texture. */ - Texture(std::vector buffer, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, bool img_2d = true, bool mipmap = true); + Texture(std::vector buffer, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = TextureFlags::TexFlags_IMG_2D | + TextureFlags::TexFlags_MIPMAP); static Texture white_texture(); diff --git a/include/simpleengine/scene.h b/include/simpleengine/scene.h index ab24895..87f8318 100644 --- a/include/simpleengine/scene.h +++ b/include/simpleengine/scene.h @@ -1,44 +1,32 @@ #pragma once +#include "entt/entity/fwd.hpp" #include "gfx/mesh.h" -#include "entity.h" #include "event/event.h" #include "renderable.h" +#include "simpleengine/gfx/renderer.h" #include #include #include +#include + namespace simpleengine { + namespace ecs { + class Entity; + } + class Scene : public simpleengine::Event { + protected: + entt::registry registry; + std::shared_ptr renderer; public: - /** - * @brief A list of entities in this scene. - * - */ - std::vector> entities; + Scene(std::shared_ptr renderer); - /** - * @brief Models that don't belong to an entity. - * - */ - std::vector> stray_models; + ecs::Entity create_entity(); - Scene() = default; - - void add_entity(std::shared_ptr entity) { - entities.push_back(entity); - } - - void add_stray_model(std::shared_ptr stray) { - stray_models.push_back(stray); - } - - virtual void update(const float& delta_time) override { - for (auto& entity : entities) { - entity->update(delta_time); - } - } + virtual void update(const float& delta_time) override; }; } \ No newline at end of file diff --git a/shell.nix b/shell.nix index c9852e1..47146ae 100644 --- a/shell.nix +++ b/shell.nix @@ -12,6 +12,5 @@ pkgs.mkShell { glfw glm assimp - (callPackage ./soil2.nix { }) ]; } \ No newline at end of file diff --git a/soil2.nix b/soil2.nix deleted file mode 100644 index 09f587e..0000000 --- a/soil2.nix +++ /dev/null @@ -1,37 +0,0 @@ -{ lib -, stdenv -, fetchFromGitHub -, cmake -, libGL -, libX11 -}: - -stdenv.mkDerivation rec { - version = "39028e64921c03cabbc53b937da4a85aba264e00"; - pname = "soil2"; - - src = fetchFromGitHub { - owner = "SpartanJ"; - repo = pname; - rev = version; - sha256 = "sha256-zQQ8lwOkMCxdlf6zfnIOYVUTGVqnJuHL/LL8fbzxwHY="; - }; - - nativeBuildInputs = [ cmake libGL libX11 ]; - - installPhase = '' - runHook preInstall - - cmake --build . --target install --config Release - - runHook postInstall - ''; - - meta = with lib; { - description = "SOIL2 is a tiny C library used primarily for uploading textures into OpenGL."; - homepage = "https://github.com/SpartanJ/SOIL2"; - license = licenses.mit0; - platforms = platforms.unix; - maintainers = with maintainers; [ seanomik ]; - }; -} \ No newline at end of file diff --git a/src/camera.cpp b/src/camera.cpp index 6ee76b7..5400a5c 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -63,19 +63,19 @@ namespace simpleengine { } if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) { - rotation.z += camera_speed; + rotation.z += camera_speed * .3; } if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) { - rotation.z -= camera_speed; + rotation.z -= camera_speed * .3; } if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) { - rotation.y -= camera_speed; + rotation.y -= camera_speed * .3; } if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) { - rotation.y += camera_speed; + rotation.y += camera_speed * .3; } // Limit the pitch of the camera. diff --git a/src/ecs/component/component.cpp b/src/ecs/component/component.cpp deleted file mode 100644 index 02f50b5..0000000 --- a/src/ecs/component/component.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "ecs/component/component.h" - -uint32_t simpleengine::Component::incrementing_handle = 0; \ No newline at end of file diff --git a/src/ecs/entity.cpp b/src/ecs/entity.cpp deleted file mode 100644 index 01c1846..0000000 --- a/src/ecs/entity.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "ecs/entity.h" - -uint32_t simpleengine::Entity::incrementing_handle = 0; \ No newline at end of file diff --git a/src/entity_manager.cpp b/src/entity_manager.cpp deleted file mode 100644 index 53e4c8e..0000000 --- a/src/entity_manager.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "entity_manager.h" - -simpleengine::EntityManager::EntityManager() { - -} - -void simpleengine::EntityManager::submit_entity(std::shared_ptr entity) { - entities.emplace(entity->get_handle(), entity); -} - -bool simpleengine::EntityManager::withdraw_entity(std::shared_ptr entity) { - auto it = entities.find(entity->get_handle()); - - if (it != entities.end()) { - it->second->destroy(); - entities.erase(it); - - return true; - } - - return false; -} - -void simpleengine::EntityManager::initialize() { - -} - -void simpleengine::EntityManager::destroy() { - std::cout << "Destroy entity manager!" << std::endl; - - for (auto& [handle, entity] : entities) { - entity->destroy(); - } -} - -void simpleengine::EntityManager::update(const float& delta_time) { - for (auto& [handle, entity] : entities) { - entity->update(delta_time); - } -} \ No newline at end of file diff --git a/src/gfx/model.cpp b/src/gfx/model.cpp index c749bb5..98d8f7a 100644 --- a/src/gfx/model.cpp +++ b/src/gfx/model.cpp @@ -14,15 +14,26 @@ #include namespace simpleengine::gfx { - Model::Model(std::string file_path) { + Model::Model(std::string file_path, int model_processing_flags, int assimp_flags): model_processing_flags(model_processing_flags), + additional_assimp_flags(assimp_flags) { load_model(file_path); } + void Model::post_process() { + if (model_processing_flags & ModelProcessingFlags::MdlProcFlag_FLIP_TEX_COORDS_VERTICALLY) { + vertically_flip_tex_coords(); + } + + if (model_processing_flags & ModelProcessingFlags::MdlProcFlag_FLIP_TEX_COORDS_HORIZONTALLY) { + horizontally_flip_tex_coords(); + } + } + void Model::load_model(std::string path) { Assimp::Importer importer; // assimp post processing options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html - const aiScene *scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs); + const aiScene *scene = importer.ReadFile(path, additional_assimp_flags | aiProcess_Triangulate | aiProcess_FlipUVs); if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl; @@ -31,28 +42,30 @@ namespace simpleengine::gfx { model_directory = path.substr(0, path.find_last_of('/')); - process_node(scene->mRootNode, scene); + std::unordered_map>> processed_textures; + process_node(processed_textures, scene->mRootNode, scene); + + post_process(); } - void Model::process_node(aiNode* node, const aiScene* scene) { + void Model::process_node(std::unordered_map>>& processed_textures, aiNode* node, const aiScene* scene) { // process all the node's meshes (if any) for (int i = 0; i < node->mNumMeshes; i++) { aiMesh *mesh = scene->mMeshes[node->mMeshes[i]]; - meshes.push_back(process_mesh(mesh, scene)); + meshes.push_back(process_mesh(processed_textures, mesh, scene)); } // then do the same for each of its children for (int i = 0; i < node->mNumChildren; i++) { - process_node(node->mChildren[i], scene); + process_node(processed_textures, node->mChildren[i], scene); } } - gfx::Mesh Model::process_mesh(aiMesh* mesh, const aiScene* scene) { + gfx::Mesh Model::process_mesh(std::unordered_map>>& processed_textures, aiMesh* mesh, const aiScene* scene) { std::vector vertices; std::vector indices; - std::vector textures; - for(unsigned int i = 0; i < mesh->mNumVertices; i++) { + for (unsigned int i = 0; i < mesh->mNumVertices; i++) { LitVertex vertex; vertex.color = glm::vec3(1.f); vertex.texture_id = 0; @@ -60,10 +73,12 @@ namespace simpleengine::gfx { simpleengine::Vectorf position(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z); vertex.position = position; - glm::vec3 normal(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z); - vertex.normal = normal; + if (mesh->HasNormals()) { + glm::vec3 normal(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z); + vertex.normal = normal; + } - if(mesh->mTextureCoords[0]) { + if (mesh->mTextureCoords[0]) { glm::vec2 tex_coord(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y); vertex.tex_coord = tex_coord; @@ -83,18 +98,18 @@ namespace simpleengine::gfx { // Create a default material and white texture. auto white_texture = gfx::Texture::white_texture(); - std::unordered_map> default_textures; - default_textures.emplace(white_texture.type, std::vector{ white_texture }); + std::unordered_map>> default_textures; + default_textures.emplace(white_texture.type, std::vector>{ std::make_shared(white_texture) }); gfx::Material mat(default_textures, 1.f, 0.f, 0.f, 0.f, 0.f); - if(mesh->mMaterialIndex >= 0) { + if (mesh->mMaterialIndex >= 0) { std::cout << "TODO: Process model materials!" << std::endl; - std::unordered_map> textures; + std::unordered_map>> textures; aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex]; // Load Diffuse texture maps - std::vector diffuse_maps = load_material_textures(material, aiTextureType_DIFFUSE); + std::vector> diffuse_maps = load_material_texture(processed_textures, material, aiTextureType_DIFFUSE); if (!diffuse_maps.empty()) textures.emplace(aiTextureType_DIFFUSE, diffuse_maps); // TODO Handle other types of texture maps @@ -102,27 +117,97 @@ namespace simpleengine::gfx { if (!textures.empty()) { // TODO: Find a way to let the user set the scalars. mat = Material(textures, 1.f, 0.f, 0.f, 0.f, 0.f); + + // Add `textures` into the `processed_textures` list. + for (const auto& pair : textures) { + for (const auto& texture : pair.second) { + bool contains = false; + + auto found = processed_textures.find(pair.first); + if (found != processed_textures.end()) { + for (const auto& processed_text : found->second) { + if (processed_text->path == texture->path) { + contains = true; + break; + } + } + + if (!contains) { + //found->second + found->second.emplace_back(texture); + } + } else { + processed_textures.emplace(pair.first, std::vector>{ pair.second }); + } + } + } } } - return Mesh(vertices, indices, mat); + Mesh se_mesh(vertices, indices, mat); + + if (!mesh->HasNormals()) { + se_mesh.calculate_normals(); + } + + return se_mesh; } - std::vector Model::load_material_textures(aiMaterial* material, aiTextureType type) { - std::vector textures; + std::unordered_map> load_all_textures(aiMaterial* material) { + // Load Diffuse texture maps + return {}; + } - for(int i = 0; i < material->GetTextureCount(type); i++) { - aiString texture_path; - material->GetTexture(type, i, &texture_path); + std::vector> Model::load_material_texture(std::unordered_map>>& processed_textures, aiMaterial* material, aiTextureType type) { + std::vector> textures; + + for (int i = 0; i < material->GetTextureCount(type); i++) { + aiString texture_path_ai; + material->GetTexture(type, i, &texture_path_ai); + std::string texture_path = texture_path_ai.C_Str(); + + // If the texture has been read, we should skip it. + bool skip = false; + for (const auto& pair : processed_textures) { + if (pair.first == type) { + for (const auto& texture : pair.second) { + if (texture->path == texture_path) { + textures.emplace_back(texture); // Push a copy of the texture for this Mesh. + skip = true; + break; + } + } + } + } + if (skip) continue; std::stringstream ss; - ss << model_directory << "/" << texture_path.C_Str(); + ss << model_directory << "/" << texture_path; std::string full_path = ss.str(); - Texture texture(full_path.c_str(), type); - textures.emplace_back(texture); + Texture texture(full_path.c_str(), type, /* TextureFlags::TexFlags_FLIP_VERTICALLY | */ TextureFlags::TexFlags_IMG_2D | TextureFlags::TexFlags_MIPMAP); + texture.path = texture_path; + textures.emplace_back(std::make_shared(texture)); + + std::cout << "Texture full path: " << full_path << ", texture_path: " << texture_path << std::endl; } return textures; } + + void Model::vertically_flip_tex_coords() { + for (auto& mesh : meshes) { + for (auto& vertex : mesh.vertices) { + vertex.tex_coord.y *= -1; + } + } + } + + void Model::horizontally_flip_tex_coords() { + for (auto& mesh : meshes) { + for (auto& vertex : mesh.vertices) { + vertex.tex_coord.x *= -1; + } + } + } } // namespace simpleengine::gfx \ No newline at end of file diff --git a/src/gfx/renderer.cpp b/src/gfx/renderer.cpp index 6ad4644..11f18a5 100644 --- a/src/gfx/renderer.cpp +++ b/src/gfx/renderer.cpp @@ -1,6 +1,4 @@ #include "gfx/renderer.h" -#include "ecs/component/component.h" -#include "ecs/entity.h" #include "gfx/mesh.h" #include "gfx/vao.h" #include "renderable.h" @@ -12,16 +10,7 @@ #include namespace simpleengine::gfx { - void create_mesh_buffers(std::shared_ptr comp, simpleengine::gfx::Mesh& mesh); - - void Renderer::RenderingModel::destroy_buffers() { - std::cout << "Destroying entity models..." << std::endl; - - // Iterate through all buffer lists and destroy each inner buffer. - for (auto& pair : component_models) { - pair.second.destroy(); - } - } + void create_mesh_buffers(simpleengine::gfx::Mesh& mesh); Renderer::Renderer(GLFWwindow* window, gfx::Shader shader): window(window), shader(shader) { @@ -45,24 +34,40 @@ namespace simpleengine::gfx { glDebugMessageCallback(debug_message_callback, 0); } - void Renderer::submit_entity(std::shared_ptr entity) { - std::cout << "Submitting entity (" << entity->get_handle() << ")..." << std::endl; - auto it = rendering_models.emplace(entity->get_handle(), entity); - it.first->second.update_buffers(); + void Renderer::queue_job(RenderingJob job) { + RenderingJob& emplace = rendering_queue.emplace(job); + create_job_buffers(emplace); } - bool Renderer::withdraw_entity(std::shared_ptr entity) { - std::cout << "Withdrawing entity (" << entity->get_handle() << ")..."; - auto it = rendering_models.find(entity->get_handle()); + void Renderer::create_job_buffers(RenderingJob& job) { + Mesh& rendering_mesh = job.rendering_mesh; - if (it != rendering_models.end()) { - it->second.destroy_buffers(); - rendering_models.erase(it); + if (!rendering_mesh.are_buffers_created) { + gfx::VBO& vbo = rendering_mesh.vbo; + gfx::VBO& ebo = rendering_mesh.ebo; + gfx::VAO& vao = rendering_mesh.vao; - return true; + vao.bind(); + vbo.buffer(rendering_mesh.vertices.data(), 0, sizeof(LitVertex) * rendering_mesh.vertices.size()); + + if (!rendering_mesh.indicies.empty()) { + ebo.buffer(rendering_mesh.indicies.data(), 0, rendering_mesh.indicies.size() * sizeof(GLuint)); + } + + // Enable VAO attributes + vao.enable_attrib(vbo, 0, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, position), false); + vao.enable_attrib(vbo, 1, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, color), false); + vao.enable_attrib(vbo, 2, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, normal), false); + vao.enable_attrib(vbo, 3, 2, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, tex_coord), false); + vao.enable_attrib(vbo, 4, 1, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, texture_id), false); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + rendering_mesh.are_buffers_created = true; + + std::cout << "Created render job buffers" << std::endl; } - - return false; } void Renderer::update(const float& delta_time) { @@ -76,113 +81,53 @@ namespace simpleengine::gfx { void Renderer::destroy() { std::cout << "Destroying renderer..." << std::endl; - for (auto& [handle, rendering] : rendering_models) { + /* for (auto& [handle, rendering] : rendering_models) { rendering.destroy_buffers(); - } + } */ } void Renderer::render() { shader.use(); - for (auto& [handle, rendering] : rendering_models) { - if (rendering.component_models.size() >= 0) { - std::shared_ptr& entity = rendering.entity; + while (!rendering_queue.empty()) { + // Get the job from the queue, we'll remove it after we render. + RenderingJob& job = rendering_queue.front(); + Mesh& mesh = job.rendering_mesh; - shader.set_uniform_matrix_4f("transform_matrix", entity->transform_matrix, false); + shader.set_uniform_matrix_4f("transform_matrix", job.transform_mat, false); - for (const auto& pair : rendering.component_models) { - Mesh& model = pair.second; - std::optional& material = model.material; + std::optional& material = mesh.material; - shader.set_uniform_int("u_textures", 0, false); + shader.set_uniform_int("u_textures", 0, false); - if (material.has_value()) { - shader.set_uniform_float("u_texture_shine", material->shine, false); - shader.set_uniform_float("u_texture_reflectivity", material->reflectivity, false); + if (material.has_value()) { + shader.set_uniform_float("u_texture_shine", material->shine, false); + shader.set_uniform_float("u_texture_reflectivity", material->reflectivity, false); - int texture_count = 0; - auto diffuse_maps = material->textures.find(aiTextureType_DIFFUSE); - for (const auto& texture : diffuse_maps->second) { - // We can only bind to 16 textures at a time (indexes are 0-15) - if (texture_count >= 16) break; + int texture_count = 0; + auto diffuse_maps = material->textures.find(aiTextureType_DIFFUSE); + for (const auto& texture : diffuse_maps->second) { + // We can only bind to 16 textures at a time (indexes are 0-15) + if (texture_count >= 16) break; - glActiveTexture(GL_TEXTURE0 + texture_count); - glBindTextureUnit(texture_count, texture.get_texture_id()); + glActiveTexture(GL_TEXTURE0 + texture_count); + glBindTextureUnit(texture_count, texture->get_texture_id()); - texture_count++; - } - } - - model.vao.bind(); - if (model.indicies.empty()) { - glDrawArrays(GL_TRIANGLES, 0, model.vertices.size()); - } else { - glDrawElements(GL_TRIANGLES, model.indicies.size(), GL_UNSIGNED_INT, 0); - } + texture_count++; } } + + mesh.vao.bind(); + if (mesh.indicies.empty()) { + glDrawArrays(GL_TRIANGLES, 0, mesh.vertices.size()); + } else { + glDrawElements(GL_TRIANGLES, mesh.indicies.size(), GL_UNSIGNED_INT, 0); + } + + // Now we'll remove the job from the queue. + rendering_queue.pop(); } shader.unuse(); } - - void Renderer::RenderingModel::update_buffers() { - if (entity->has_component()) { - std::shared_ptr comp = entity->get_component(); - auto iter = component_models.find(comp->get_handle()); - if (iter == component_models.end()) { - std::cout << "Enabling buffer attributes for MeshComponent (" << comp->get_handle() << ")..." << std::endl; - - //iter->second = comp->model; - gfx::Mesh& mesh = comp->model; - create_mesh_buffers(comp, mesh); - component_models.emplace(comp->get_handle(), mesh); - - std::cout << "Enabled all buffer attributes for MeshComponent" << std::endl; - } else { - std::cout << "Already exists" << std::endl; - } - } else if (entity->has_component()) { - std::shared_ptr comp = entity->get_component(); - - auto iter = component_models.find(comp->get_handle()); - if (iter == component_models.end()) { - std::cout << "Enabling buffer attributes for ModelComponent (" << comp->get_handle() << ")..." << std::endl; - - // Store all the model's meshes - for (auto& mesh : comp->model.meshes) { - create_mesh_buffers(comp, mesh); - - component_models.emplace(comp->get_handle(), mesh); - } - - std::cout << "Enabled all buffer attributes for ModelComponent" << std::endl; - } else { - std::cout << "Already exists" << std::endl; - } - } - } - - void create_mesh_buffers(std::shared_ptr comp, gfx::Mesh& mesh) { - gfx::VBO& vbo = mesh.vbo; - gfx::VBO& ebo = mesh.ebo; - gfx::VAO& vao = mesh.vao; - - vao.bind(); - vbo.buffer(mesh.vertices.data(), 0, sizeof(LitVertex) * mesh.vertices.size()); - - if (!mesh.indicies.empty()) { - ebo.buffer(mesh.indicies.data(), 0, mesh.indicies.size() * sizeof(GLuint)); - } - - // Enable VAO attributes - vao.enable_attrib(vbo, 0, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, position), false); - vao.enable_attrib(vbo, 1, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, color), false); - vao.enable_attrib(vbo, 2, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, normal), false); - vao.enable_attrib(vbo, 3, 2, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, tex_coord), false); - vao.enable_attrib(vbo, 4, 1, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, texture_id), false); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); - } } \ No newline at end of file diff --git a/src/gfx/texture.cpp b/src/gfx/texture.cpp index 6c0f0e2..ad0bede 100644 --- a/src/gfx/texture.cpp +++ b/src/gfx/texture.cpp @@ -4,7 +4,11 @@ #include namespace simpleengine::gfx { - Texture::Texture(const char* path, aiTextureType type, bool img_2d, bool mipmap): type(type) { + Texture::Texture(const char* path, aiTextureType type, int flags): type(type), path(path) { + bool img_2d = flags & TextureFlags::TexFlags_IMG_2D; + bool flip_vertically = flags & TextureFlags::TexFlags_FLIP_VERTICALLY; + bool mipmap = flags & TextureFlags::TexFlags_MIPMAP; + image_type_gl = img_2d ? GL_TEXTURE_2D : GL_TEXTURE_3D; glGenTextures(1, &texture_id); @@ -17,7 +21,9 @@ namespace simpleengine::gfx { glTexParameteri(image_type_gl, GL_TEXTURE_MIN_FILTER, linear_param); glTexParameteri(image_type_gl, GL_TEXTURE_MAG_FILTER, linear_param); - // Read 4 channels (RGBA) + stbi_set_flip_vertically_on_load(flip_vertically); + + // Read 4 channels (RGBA) img_data = stbi_load(path, &width, &height, &channels, 4); if(!img_data) { const char* failure = stbi_failure_reason(); @@ -32,10 +38,16 @@ namespace simpleengine::gfx { glGenerateMipmap(image_type_gl); } + stbi_set_flip_vertically_on_load(false); + unbind(); } - Texture::Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type, bool img_2d, bool mipmap): type(type) { + Texture::Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type, int flags): type(type) { + bool img_2d = flags & TextureFlags::TexFlags_IMG_2D; + bool flip_vertically = flags & TextureFlags::TexFlags_FLIP_VERTICALLY; + bool mipmap = flags & TextureFlags::TexFlags_MIPMAP; + image_type_gl = img_2d ? GL_TEXTURE_2D : GL_TEXTURE_3D; glGenTextures(1, &texture_id); @@ -48,6 +60,8 @@ namespace simpleengine::gfx { glTexParameteri(image_type_gl, GL_TEXTURE_MIN_FILTER, linear_param); glTexParameteri(image_type_gl, GL_TEXTURE_MAG_FILTER, linear_param); + stbi_set_flip_vertically_on_load(flip_vertically); + // Read 4 channels (RGBA) img_data = stbi_load_from_memory(buffer, buffer_length, &width, &height, &channels, 4); if(!img_data) { @@ -63,11 +77,13 @@ namespace simpleengine::gfx { glGenerateMipmap(image_type_gl); } + stbi_set_flip_vertically_on_load(false); + unbind(); } - Texture::Texture(std::vector buffer, aiTextureType type, bool img_2d, bool mipmap) : - Texture(buffer.data(), buffer.size(), type, img_2d, mipmap) { + Texture::Texture(std::vector buffer, aiTextureType type, int flags) : + Texture(buffer.data(), buffer.size(), type, flags) { } diff --git a/src/scene.cpp b/src/scene.cpp new file mode 100644 index 0000000..c2caeff --- /dev/null +++ b/src/scene.cpp @@ -0,0 +1,34 @@ +#include "scene.h" +#include "ecs/component/mesh_component.h" +#include "ecs/component/model_component.h" +#include "ecs/component/transform_component.h" +#include "ecs/component/rotating_component.h" +#include "ecs/entity.h" +#include "gfx/renderer.h" + +namespace simpleengine { + Scene::Scene(std::shared_ptr renderer) : renderer(renderer) { + + } + + ecs::Entity Scene::create_entity() { + return ecs::Entity(registry, registry.create()); + } + + void Scene::update(const float& delta_time) { + // Is there a way these can be grouped? + registry.view().each([this](const TransformComponent& transform, ModelComponent& model_component) { + for (auto& mesh : model_component.model.meshes) { + renderer->queue_job(gfx::RenderingJob(mesh, transform.transform_matrix)); + } + }); + + registry.view().each([this](const TransformComponent& transform, MeshComponent& mesh_component) { + renderer->queue_job(gfx::RenderingJob(mesh_component.mesh, transform.transform_matrix)); + }); + + registry.view().each([this, &delta_time](TransformComponent& transform, RotatingComponent& rotating) { + transform.rotate(rotating.rate * delta_time, rotating.rotation_axis); + }); + } +} \ No newline at end of file