diff --git a/.gitignore b/.gitignore index ec0a7a4..5459c45 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ ## Folders .vs/ bin/ +!vendor/premake/bin/ inter/ ## Files diff --git a/ember/imgui.ini b/ember/imgui.ini new file mode 100644 index 0000000..fa73773 --- /dev/null +++ b/ember/imgui.ini @@ -0,0 +1,47 @@ +[Window][Debug##Default] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[Window][Settings] +Pos=864,19 +Size=416,701 +Collapsed=0 +DockId=0x00000004,0 + +[Window][DockSpace Demo] +Size=1280,720 +Collapsed=0 + +[Window][Viewport] +Pos=315,19 +Size=547,701 +Collapsed=0 +DockId=0x00000003,0 + +[Window][Editor] +Pos=0,0 +Size=1280,720 +Collapsed=0 + +[Window][Scene hierarchy panel] +Pos=0,19 +Size=313,316 +Collapsed=0 +DockId=0x00000005,0 + +[Window][Properties] +Pos=0,337 +Size=313,383 +Collapsed=0 +DockId=0x00000006,0 + +[Docking][Data] +DockSpace ID=0x582DCB9B Window=0xCB5DF48C Pos=216,258 Size=1280,701 Split=X Selected=0x995B0CF8 + DockNode ID=0x00000002 Parent=0x582DCB9B SizeRef=862,701 Split=X + DockNode ID=0x00000001 Parent=0x00000002 SizeRef=313,701 Split=Y Selected=0x556289C7 + DockNode ID=0x00000005 Parent=0x00000001 SizeRef=283,316 Selected=0x556289C7 + DockNode ID=0x00000006 Parent=0x00000001 SizeRef=283,383 Selected=0xC89E3217 + DockNode ID=0x00000003 Parent=0x00000002 SizeRef=547,701 CentralNode=1 HiddenTabBar=1 Selected=0x995B0CF8 + DockNode ID=0x00000004 Parent=0x582DCB9B SizeRef=416,701 Selected=0x1C33C293 + diff --git a/ember/premake5.lua b/ember/premake5.lua new file mode 100644 index 0000000..908d4bf --- /dev/null +++ b/ember/premake5.lua @@ -0,0 +1,49 @@ + +project "ember" + kind "ConsoleApp" + language "C++" + cppdialect "C++17" + staticruntime "on" + + targetdir ("%{wks.location}/bin/" .. outputdir .. "/%{prj.name}") + objdir ("%{wks.location}/inter/" .. outputdir .. "/%{prj.name}") + + files + { + "src/**.h", + "src/**.cpp" + } + + includedirs + { + "src", + "%{wks.location}/pyro/external/spdlog/include", + "%{wks.location}/pyro/src", + "%{wks.location}/pyro/external", + "%{IncludeDir.glm}", + "%{IncludeDir.ImGui}", + "%{IncludeDir.entt}", + } + + links + { + "pyro" + } + + filter "system:windows" + systemversion "latest" + + filter "configurations:Debug" + defines "PYRO_DEBUG" + runtime "Debug" + symbols "on" + + filter "configurations:Release" + defines "PYRO_RELEASE" + runtime "Release" + optimize "on" + + filter "configurations:Dist" + defines "PYRO_DIST" + runtime "Release" + optimize "on" \ No newline at end of file diff --git a/ember/src/editor_layer.cpp b/ember/src/editor_layer.cpp new file mode 100644 index 0000000..0696ac2 --- /dev/null +++ b/ember/src/editor_layer.cpp @@ -0,0 +1,341 @@ +#include "pyro.h" +#include "editor_layer.h" +#include "imgui/imgui.h" +#include "scenes/noise1d_scene.h" +#include "scenes/noise2d_scene.h" +#include "scenes/roguelike_scene.h" +#include "utils/random.h" + +#include + + +namespace pyro +{ + editor_layer::editor_layer(float width, float height) + : imgui_layer("ember") + , m_seed(0) + { +#if OLD_SCENE + m_2d_camera_controller = + make_ref( + glm::vec3{ 0.f,0.f,0.f }, width / height); + m_scene_manager.add_scene(make_ref(m_2d_camera_controller)); + m_scene_manager.add_scene(make_ref(m_2d_camera_controller)); + m_scene_manager.add_scene(make_ref(m_2d_camera_controller)); +#endif + m_viewport_size = { width, height }; + framebuffer_props props; + props.width = static_cast(width); + props.height = static_cast(height); + m_framebuffer = frame_buffer_2d::create(props); + } + + editor_layer::~editor_layer() + { + } + + + void editor_layer::on_attach() + { + PYRO_PROFILE_FUNCTION(); + imgui_layer::on_attach(); +#if OLD_SCENE + m_scene_manager.init_first_scene(); +#else + m_active_scene = make_ref(); + + auto green_square = m_active_scene->create_entity("Green Square"); + green_square.add_component(glm::vec4{ 0.f,1.f,0.f,1.f }); + + auto red_square = m_active_scene->create_entity("Red Square"); + red_square.add_component(glm::vec4{ 1.f,0.f,0.f,1.f }); + + m_camera_entity = m_active_scene->create_entity("Camera Entity"); + m_camera_entity.add_component(); + + m_square_entity = green_square; + + m_second_camera = m_active_scene->create_entity("Clip-space Camera"); + auto &sc = m_second_camera.add_component(); + sc.primary = false; + + class camera_controller : public scriptable_entity + { + public: + virtual void on_create() override + { + } + + virtual void on_update(timestep const &ts) override + { + auto &transform = get_component().transform; + float speed = 5.0f; + + if(input::key_pressed(pyro::key_codes::KEY_A)) // left + transform[3][0] -= speed * ts; + else if(input::key_pressed(pyro::key_codes::KEY_D)) // right + transform[3][0] += speed * ts; + + if(input::key_pressed(pyro::key_codes::KEY_W)) // up + transform[3][1] += speed * ts; + else if(input::key_pressed(pyro::key_codes::KEY_S)) // down + transform[3][1] -= speed * ts; + } + + }; + + m_camera_entity.add_component().bind(); + + // panels + m_scene_hierarchy_panel.context(m_active_scene); +#endif + } + + void editor_layer::on_detach() + { + PYRO_PROFILE_FUNCTION(); + } + + void editor_layer::on_update(timestep const &ts) + { + // Update + PYRO_PROFILE_FUNCTION(); + + if(m_viewport_size.x > 0.0f && m_viewport_size.y > 0.0f && + (m_framebuffer->width() != m_viewport_size.x + || m_framebuffer->height() != m_viewport_size.y)) + { + uint32_t width = static_cast(m_viewport_size.x); + uint32_t height = static_cast(m_viewport_size.y); + m_framebuffer->resize(width, height); + +#if OLD_SCENE + m_2d_camera_controller->on_resize(m_viewport_size.x, m_viewport_size.y); +#else + m_active_scene->on_viewport_resize(width, height); +#endif + } + if(m_viewport_focused) + { +#if OLD_SCENE + m_2d_camera_controller->on_update(ts); +#endif + } +#if OLD_SCENE + m_scene_manager.on_update(ts); +#else + m_active_scene->on_update(ts); +#endif + } + + void editor_layer::on_render() const + { + renderer_2d::reset_stats(); + + m_framebuffer->bind(); + render_command::clear_color({ 0.1f, 0.1f, 0.1f, 1.f }); + render_command::clear(); + +#if OLD_SCENE + m_scene_manager.on_render(); +#else + //renderer_2d::begin_scene(m_2d_camera_controller->camera()); + m_active_scene->on_render(); + //renderer_2d::end_scene(); +#endif + m_framebuffer->unbind(); + + } + + void editor_layer::on_imgui_render() + { + // Note: Switch this to true to enable dockspace + static bool dockspaceOpen = true; + static bool opt_fullscreen_persistant = true; + bool opt_fullscreen = opt_fullscreen_persistant; + static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None; + + // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into, + // because it would be confusing to have two docking targets within each others. + ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; + if(opt_fullscreen) + { + ImGuiViewport *viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(viewport->Pos); + ImGui::SetNextWindowSize(viewport->Size); + ImGui::SetNextWindowViewport(viewport->ID); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; + } + + // When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background and handle the pass-thru hole, so we ask Begin() to not render a background. + if(dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) + window_flags |= ImGuiWindowFlags_NoBackground; + + // Important: note that we proceed even if Begin() returns false (aka window is collapsed). + // This is because we want to keep our DockSpace() active. If a DockSpace() is inactive, + // all active windows docked into it will lose their parent and become undocked. + // We cannot preserve the docking relationship between an active window and an inactive docking, otherwise + // any change of dockspace/settings would lead to windows being stuck in limbo and never being visible. + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + ImGui::Begin("Editor", &dockspaceOpen, window_flags); + ImGui::PopStyleVar(); + + if(opt_fullscreen) + ImGui::PopStyleVar(2); + + // DockSpace + ImGuiIO &io = ImGui::GetIO(); + if(io.ConfigFlags & ImGuiConfigFlags_DockingEnable) + { + ImGuiID dockspace_id = ImGui::GetID("DockSpace"); + ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); + } + + if(ImGui::BeginMenuBar()) + { + if(ImGui::BeginMenu("File")) + { + // Disabling fullscreen would allow the window to be moved to the front of other windows, + // which we can't undo at the moment without finer window depth/z control. + //ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen_persistant); + + if(ImGui::MenuItem("Exit")) + application::instance().exit(); + ImGui::EndMenu(); + } + + ImGui::EndMenuBar(); + } + +#if OLD_SCENE + auto current_scene = std::static_pointer_cast(m_scene_manager.current_scene()); + + // hide all ui if the scene is being played + if(!current_scene->is_playing()) + { + ImGui::Begin("Settings"); + + auto stats = renderer_2d::stats(); + ImGui::Text("-- 2D Renderer stats:"); + float ms = application::frame_time() * 1000.f; + ImGui::Text("- Frame time: %f ms", ms); + ImGui::Text("- FPS: %d/s", application::fps()); + ImGui::Text("- Draw calls: %d", stats.draw_calls); + ImGui::Text("- Quads: %d", stats.quad_count); + ImGui::Text("- Vertices: %d", stats.total_vertex_count()); + ImGui::Text("- Indices: %d", stats.total_index_count()); + ImGui::Text("---------------------"); + + ImGui::Text("Select level type"); + static int scene_index = 0; + bool pressed = false; + pressed |= ImGui::RadioButton("1", &scene_index, 0); ImGui::SameLine(); + pressed |= ImGui::RadioButton("2", &scene_index, 1); ImGui::SameLine(); + pressed |= ImGui::RadioButton("3", &scene_index, 2); + + if(pressed) + { + m_scene_manager.go_to(scene_index); + } + + //ImGui::Text("[Press Q to quit play mode]"); + //if(ImGui::Button("PLAY", { 100.f,25.f })) + //{ + // current_scene->play(); + //} + ImGui::Text("---------------------"); + + m_scene_manager.on_imgui_render(); + + } +#else + m_scene_hierarchy_panel.on_imgui_render(); + + ImGui::Begin("Settings"); + if(m_square_entity) + { + { + ImGui::Separator(); + auto &tag = m_square_entity.get_component().tag; + ImGui::Text("%s", tag.c_str()); + + auto &squareColor = m_square_entity.get_component().color; + ImGui::ColorEdit4("Square Color", glm::value_ptr(squareColor)); + ImGui::Separator(); + } + } + + if(ImGui::Checkbox("Toggle Primary cameras", &m_is_primary_camera)) + { + m_camera_entity.get_component().primary = m_is_primary_camera; + m_second_camera.get_component().primary = !m_is_primary_camera; + } +#endif + + for(auto &result : m_profile_results) + { + char label[50]; + strcpy_s(label, "%.3fms "); + strcat_s(label, result.name); + ImGui::Text(label, result.time); + } + m_profile_results.clear(); + + auto stats = renderer_2d::stats(); + ImGui::Text("-- 2D Renderer stats:"); + ImGui::Text("- Draw calls: %d", stats.draw_calls); + ImGui::Text("- Quads: %d", stats.quad_count); + ImGui::Text("- Vertices: %d", stats.total_vertex_count()); + ImGui::Text("- Indices: %d", stats.total_index_count()); + ImGui::Text("---------------------"); + ImGui::End(); + + + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 0, 0 }); + + { + ImGui::Begin("Viewport"); + + m_viewport_focused = ImGui::IsWindowFocused(); + m_viewport_hovered = ImGui::IsWindowHovered(); + application::instance().gui_layer()->block_events(!m_viewport_focused || !m_viewport_hovered); + + ImVec2 viewportPanelSize = ImGui::GetContentRegionAvail(); + m_viewport_size = { viewportPanelSize.x, viewportPanelSize.y }; + uint32_t textureID = m_framebuffer->color_attachment(); + ImGui::Image(reinterpret_cast(textureID), ImVec2{ m_viewport_size.x, m_viewport_size.y }, ImVec2{ 0, 1 }, ImVec2{ 1, 0 }); + ImGui::End(); + } + ImGui::PopStyleVar(); + + ImGui::End(); + } + + void editor_layer::on_event(event &e) + { + imgui_layer::on_event(e); +#if OLD_SCENE + m_2d_camera_controller->on_event(e); +#endif + event_dispatcher dispatcher(e); + // dispatch event on window X pressed +#if OLD_SCENE + auto current_scene = std::static_pointer_cast(m_scene_manager.current_scene()); + dispatcher.dispatch([&](key_pressed_event ev) + { + if(current_scene->is_playing() && ev.key_code() == key_codes::KEY_Q) + { + current_scene->stop_playing(); + } + // return if event is handled or not + return false; + }); + + m_scene_manager.on_event(e); +#else +#endif + } +} diff --git a/ember/src/editor_layer.h b/ember/src/editor_layer.h new file mode 100644 index 0000000..4ae172b --- /dev/null +++ b/ember/src/editor_layer.h @@ -0,0 +1,52 @@ +#pragma once +#include "pyro.h" +#include "scenes/base_noise_scene.h" +#include "scenes/scene_manager.h" +#include "panels/scene_hierarchy_panel.h" +#include "utils/perlin_noise.h" + + +namespace pyro +{ + class editor_layer final : public imgui_layer + { + + public: + editor_layer(float width, float height); + ~editor_layer() override; + void on_attach() override; + void on_detach() override; + void on_update(const timestep &ts) override; + void on_render() const override; + void on_imgui_render() override; + void on_event(event &e) override; + + private: + +#define OLD_SCENE 0 +#if OLD_SCENE + ref m_2d_camera_controller; + scene_manager m_scene_manager; +#else + ref m_active_scene; + entity m_camera_entity; + entity m_second_camera; + entity m_square_entity; + bool m_is_primary_camera = true; + scene_hierarchy_panel m_scene_hierarchy_panel; +#endif + ref m_framebuffer; + glm::vec2 m_viewport_size = { 0.0f, 0.0f }; + bool m_viewport_focused = false; + bool m_viewport_hovered = false; + + int32_t m_seed; + + struct profile_result + { + char const *name; + float time; + }; + std::vector m_profile_results; + }; +} diff --git a/ember/src/ember_app.cpp b/ember/src/ember_app.cpp index 3c59472..9612b0c 100644 --- a/ember/src/ember_app.cpp +++ b/ember/src/ember_app.cpp @@ -1,56 +1,58 @@ // --------- Entry Point --------------- #include "pyro/core/entry_point.h" -#include "layer_2d.h" +#include "editor_layer.h" -// Level editor -// [Ember, a small piece burning or glowing in a fire] -class ember : public pyro::application +namespace pyro { -public: - ember(uint32_t width, uint32_t height) - :application("Ember Editor",width,height) + + // Level editor + // [Ember, a small piece burning or glowing in a fire] + class ember : public application { - } + public: + ember(uint32_t width, uint32_t height) + :application("Ember Editor", width, height) + { + } - ~ember() = default; + ~ember() = default; - // Inherited via application - virtual void init() override - { - const float width = static_cast(window().width()); - const float height = static_cast(window().height()); - m_layer_2d = pyro::make_ref(width, height); - push_layer(m_layer_2d); - } + // Inherited via application + virtual void init() override + { + const float width = static_cast(window().width()); + const float height = static_cast(window().height()); + m_imgui_layer = make_ref(width, height); + push_layer(m_imgui_layer); + } - virtual void deinit() override - { - } + virtual void deinit() override + { + } - void on_event(pyro::event &e) override - { - application::on_event(e); + void on_event(event &e) override + { + application::on_event(e); - pyro::event_dispatcher dispatcher(e); - // dispatch event on window X pressed - dispatcher.dispatch(BIND_EVENT_FN(ember::on_key_pressed)); - } + event_dispatcher dispatcher(e); + // dispatch event on window X pressed + dispatcher.dispatch(BIND_EVENT_FN(ember::on_key_pressed)); + } - bool on_key_pressed(pyro::key_pressed_event &e) - { - if(e.key_code() == pyro::key_codes::KEY_ESCAPE) + bool on_key_pressed(key_pressed_event &e) { - exit(); + if(e.key_code() == key_codes::KEY_ESCAPE) + { + exit(); + } + return false; } - return false; - } + }; -private: - pyro::ref m_layer_2d; -}; + application *create_application() + { + return new ember(1280, 720); + } -pyro::application *pyro::create_application() -{ - return new ember(1280, 720); } \ No newline at end of file diff --git a/ember/src/layer_2d.cpp b/ember/src/layer_2d.cpp deleted file mode 100644 index f81136a..0000000 --- a/ember/src/layer_2d.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "pyro.h" -#include "layer_2d.h" -#include "imgui/imgui.h" -#include "scenes/noise1d_scene.h" -#include "scenes/noise2d_scene.h" -#include "scenes/roguelike_scene.h" -#include "utils/random.h" - - -layer_2d::layer_2d(float width, float height) - : imgui_layer("Sandbox2D") - , m_seed(0) - , m_2d_camera_controller( - new pyro::orthographic_camera_controller( - glm::vec3{ 0.f,0.f,0.f }, width / height, 10.f)) -{ - auto cam = m_2d_camera_controller->camera(); - m_scene_manager.add_scene(pyro::make_ref(m_2d_camera_controller)); - m_scene_manager.add_scene(pyro::make_ref(m_2d_camera_controller)); - m_scene_manager.add_scene(pyro::make_ref(m_2d_camera_controller)); -} - -layer_2d::~layer_2d() -{ -} - - -void layer_2d::on_attach() -{ - PYRO_PROFILE_FUNCTION(); - imgui_layer::on_attach(); - m_scene_manager.init_first_scene(); -} - -void layer_2d::on_detach() -{ - PYRO_PROFILE_FUNCTION(); -} - -void layer_2d::on_update(const pyro::timestep &ts) -{ - // Update - PYRO_PROFILE_FUNCTION(); - m_2d_camera_controller->on_update(ts); - m_scene_manager.on_update(ts); -} - -void layer_2d::on_render() const -{ - pyro::renderer_2d::reset_stats(); - { - // Pre Render - PYRO_PROFILE_SCOPE("scene::pre_render"); - pyro::render_command::clear_color({ 0.1f, 0.1f, 0.1f, 1.f }); - pyro::render_command::clear(); - } - { - // Render - PYRO_PROFILE_SCOPE("layer_2d::render"); - m_scene_manager.on_render(); - } -} - -void layer_2d::on_imgui_render() -{ - auto current_scene = std::static_pointer_cast(m_scene_manager.current_scene()); - - // hide all ui if the scene is being played - if(current_scene->is_playing()) - return; - - ImGui::Begin("Settings"); - - auto stats = pyro::renderer_2d::stats(); - ImGui::Text("-- 2D Renderer stats:"); - float ms = pyro::application::frame_time() * 1000.f; - ImGui::Text("- Frame time: %f ms", ms); - ImGui::Text("- FPS: %d/s", pyro::application::fps()); - ImGui::Text("- Draw calls: %d", stats.draw_calls); - ImGui::Text("- Quads: %d", stats.quad_count); - ImGui::Text("- Vertices: %d", stats.total_vertex_count()); - ImGui::Text("- Indices: %d", stats.total_index_count()); - ImGui::Text("---------------------"); - - ImGui::Text("Select level type"); - static int scene_index = 0; - bool pressed = false; - pressed |= ImGui::RadioButton("1", &scene_index, 0); ImGui::SameLine(); - pressed |= ImGui::RadioButton("2", &scene_index, 1); ImGui::SameLine(); - pressed |= ImGui::RadioButton("3", &scene_index, 2); - - if(pressed) - { - m_scene_manager.go_to(scene_index); - } - - //ImGui::Text("[Press Q to quit play mode]"); - //if(ImGui::Button("PLAY", { 100.f,25.f })) - //{ - // current_scene->play(); - //} - ImGui::Text("---------------------"); - - m_scene_manager.on_imgui_render(); - - ImGui::End(); -} - -void layer_2d::on_event(pyro::event &e) -{ - imgui_layer::on_event(e); - m_2d_camera_controller->on_event(e); - pyro::event_dispatcher dispatcher(e); - // dispatch event on window X pressed - auto current_scene = std::static_pointer_cast(m_scene_manager.current_scene()); - dispatcher.dispatch([&](pyro::key_pressed_event ev) { - if(current_scene->is_playing() && ev.key_code() == pyro::key_codes::KEY_Q) - { - current_scene->stop_playing(); - } - // return if event is handled or not - return false; - }); - - m_scene_manager.on_event(e); -} - diff --git a/ember/src/layer_2d.h b/ember/src/layer_2d.h deleted file mode 100644 index 6a89813..0000000 --- a/ember/src/layer_2d.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#include "pyro.h" -#include "scenes/base_noise_scene.h" -#include "scenes/scene_manager.h" -#include "utils/perlin_noise.h" - - -class layer_2d final : public pyro::imgui_layer -{ - -public: - layer_2d(float width, float height); - ~layer_2d() override; - void on_attach() override; - void on_detach() override; - void on_update(const pyro::timestep &ts) override; - void on_render() const override; - void on_imgui_render() override; - void on_event(pyro::event &e) override; - -private: - pyro::ref m_2d_camera_controller; - scene_manager m_scene_manager; - - int32_t m_seed; - - struct profile_result - { - char const *name; - float time; - }; - std::vector m_profile_results; -}; diff --git a/ember/src/panels/scene_hierarchy_panel.cpp b/ember/src/panels/scene_hierarchy_panel.cpp new file mode 100644 index 0000000..2ac92be --- /dev/null +++ b/ember/src/panels/scene_hierarchy_panel.cpp @@ -0,0 +1,183 @@ +#include "scene_hierarchy_panel.h" +#include "pyro/scene/components.h" +#include +#include + +namespace pyro +{ + + scene_hierarchy_panel::scene_hierarchy_panel(ref scene_context) + { + context(scene_context); + } + + scene_hierarchy_panel::~scene_hierarchy_panel() + { + } + + void scene_hierarchy_panel::on_imgui_render() + { + ImGui::Begin("Scene hierarchy panel"); + + m_context->m_registry.each([&](auto entity_id) + { + entity ent{ entity_id, m_context.get() }; + draw_entity_node(ent); + }); + + // ability to deselect node + if(ImGui::IsMouseDown(0) && ImGui::IsWindowHovered()) + m_selection_context = {}; + + ImGui::End(); + + ImGui::Begin("Properties"); + if(m_selection_context) + { + draw_components(m_selection_context); + } + ImGui::End(); + } + + void scene_hierarchy_panel::draw_entity_node(entity e) + { + auto &tag = e.get_component().tag; + + ImGuiTreeNodeFlags flags = ((m_selection_context == e) ? ImGuiTreeNodeFlags_Selected : 0) | ImGuiTreeNodeFlags_OpenOnArrow; + bool opened = ImGui::TreeNodeEx(reinterpret_cast((uint64_t)(uint32_t)e), flags, tag.c_str()); + if(ImGui::IsItemClicked()) + { + m_selection_context = e; + } + + if(opened) + { + // TODO: remove this duplicated tag text + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow; + bool opened = ImGui::TreeNodeEx(reinterpret_cast(9817239), flags, tag.c_str()); + if(opened) + ImGui::TreePop(); + ImGui::TreePop(); + } + } + + void scene_hierarchy_panel::draw_components(entity e) + { + if(e.has_component()) + { + auto &tag = e.get_component().tag; + + char buffer[256]; + memset(buffer, 0, sizeof(buffer)); + strcpy_s(buffer, sizeof(buffer), tag.c_str()); + if(ImGui::InputText("Tag", buffer, sizeof(buffer))) + { + tag = std::string(buffer); + } + } + + if(e.has_component()) + { + if(ImGui::TreeNodeEx( + reinterpret_cast(typeid(transform_component).hash_code()), + ImGuiTreeNodeFlags_DefaultOpen, "Transform")) + { + auto &transform = e.get_component().transform; + ImGui::DragFloat3("Position", glm::value_ptr(transform[3]), 0.1f); + static glm::vec3 scale{ transform[0][0], transform[1][1], transform[2][2] }; + if(ImGui::DragFloat3("Scale", glm::value_ptr(scale), 0.1f)) + { + transform = glm::scale(glm::mat4{1.f}, scale); + } + + ImGui::TreePop(); + } + } + + if(e.has_component()) + { + if(ImGui::TreeNodeEx( + reinterpret_cast(typeid(camera_component).hash_code()), + ImGuiTreeNodeFlags_DefaultOpen, "Camera")) + { + auto &camera_comp = e.get_component(); + auto &camera = camera_comp.camera; + + const char *projection_type_strings[]{ "Perspective", "Otrhographic" }; + const char *current_projection_type_string = + projection_type_strings[static_cast(camera.projection_type())]; + + if(ImGui::BeginCombo("Projection", current_projection_type_string)) + { + for(int i = 0; i < 2; i++) + { + bool is_selected = + current_projection_type_string == projection_type_strings[i]; + if(ImGui::Selectable(projection_type_strings[i], is_selected)) + { + current_projection_type_string = projection_type_strings[i]; + camera.projection_type(static_cast(i)); + } + + if(is_selected) + ImGui::SetItemDefaultFocus(); + } + + ImGui::EndCombo(); + } + + ImGui::Checkbox("Primary", &camera_comp.primary); + + if(camera.projection_type() == scene_camera::e_projection_type::perspective) + { + float perspective_vertical_fov = glm::degrees(camera.perspective_vertical_fov()); + if(ImGui::DragFloat("Size", &perspective_vertical_fov)) + { + camera.perspective_vertical_fov(glm::radians(perspective_vertical_fov)); + } + + float perspective_near = camera.perspective_near(); + if(ImGui::DragFloat("Near", &perspective_near)) + { + camera.perspective_near(perspective_near); + } + + float perspective_far = camera.perspective_far(); + if(ImGui::DragFloat("Far", &perspective_far)) + { + camera.perspective_far(perspective_far); + } + } + + if(camera.projection_type() == scene_camera::e_projection_type::orthographic) + { + ImGui::Checkbox("Fixed Aspect ratio", &camera_comp.fixed_aspect_ratio); + + float ortho_size = camera.orthographic_size(); + if(ImGui::DragFloat("Size", &ortho_size)) + { + camera.orthographic_size(ortho_size); + } + + float ortho_near = camera.orthographic_near(); + if(ImGui::DragFloat("Near", &ortho_near)) + { + camera.orthographic_near(ortho_near); + } + + float ortho_far = camera.orthographic_far(); + if(ImGui::DragFloat("Far", &ortho_far)) + { + camera.orthographic_far(ortho_far); + } + } + + ImGui::TreePop(); + } + } + } + void scene_hierarchy_panel::context(ref scene_context) + { + m_context = scene_context; + } +} diff --git a/ember/src/panels/scene_hierarchy_panel.h b/ember/src/panels/scene_hierarchy_panel.h new file mode 100644 index 0000000..383a146 --- /dev/null +++ b/ember/src/panels/scene_hierarchy_panel.h @@ -0,0 +1,26 @@ +#pragma once +#include "pyro/core/core.h" +#include "pyro/core/logger.h" +#include "pyro/scene/scene.h" +#include "pyro/scene/entity.h" + +namespace pyro +{ + class scene_hierarchy_panel + { + public: + scene_hierarchy_panel() = default; + scene_hierarchy_panel(ref scene_context); + ~scene_hierarchy_panel(); + void on_imgui_render(); + void context(ref scene_context); + + private: + void draw_entity_node(entity e); + void draw_components(entity e); + + private: + ref m_context; + entity m_selection_context; + }; +} \ No newline at end of file diff --git a/ember/src/renderer/scene.cpp b/ember/src/renderer/scene.cpp index a75ff08..756acda 100644 --- a/ember/src/renderer/scene.cpp +++ b/ember/src/renderer/scene.cpp @@ -2,7 +2,7 @@ #include #include -scene::scene(pyro::ref const &camera) +scene::scene(pyro::ref const &camera) : m_camera(camera) { } diff --git a/ember/src/renderer/scene.h b/ember/src/renderer/scene.h index 1d2b422..60aca11 100644 --- a/ember/src/renderer/scene.h +++ b/ember/src/renderer/scene.h @@ -5,7 +5,7 @@ class scene { public: - scene(pyro::ref const &camera); + scene(pyro::ref const &camera); virtual ~scene() = default; virtual void init() = 0; @@ -16,5 +16,5 @@ virtual void on_event(pyro::event &e) = 0; protected: - pyro::ref m_camera; + pyro::ref m_camera; }; diff --git a/ember/src/scenes/base_noise_scene.h b/ember/src/scenes/base_noise_scene.h index 7aba0e6..18d8894 100644 --- a/ember/src/scenes/base_noise_scene.h +++ b/ember/src/scenes/base_noise_scene.h @@ -7,7 +7,7 @@ class base_noise_scene : public scene { public: - base_noise_scene(pyro::ref const &camera); + base_noise_scene(pyro::ref const &camera); virtual ~base_noise_scene() = default; virtual void play() = 0; virtual void stop_playing() = 0; @@ -17,7 +17,7 @@ class base_noise_scene : public scene }; inline -base_noise_scene::base_noise_scene(pyro::ref const &camera) +base_noise_scene::base_noise_scene(pyro::ref const &camera) : scene(camera) { } \ No newline at end of file diff --git a/ember/src/scenes/scene_manager.cpp b/ember/src/scenes/scene_manager.cpp index 2cd60da..58ef026 100644 --- a/ember/src/scenes/scene_manager.cpp +++ b/ember/src/scenes/scene_manager.cpp @@ -43,8 +43,8 @@ void scene_manager::go_to(int scene_index) { if(scene_index > -1 || scene_index > m_scenes.size() - 1) { - m_scene_index = scene_index; m_scenes[m_scene_index]->deinit(); + m_scene_index = scene_index; m_scenes[m_scene_index]->init(); } else diff --git a/premake5.lua b/premake5.lua index 06331eb..fd46276 100644 --- a/premake5.lua +++ b/premake5.lua @@ -1,36 +1,9 @@ - -require('vstudio') - -local vs = premake.vstudio.vc2010 - -local function premakeVersionComment(prj) - premake.w('') -end - -local function vcpkg(prj) - if prj.name == 'tests' then - local triplet = 'x64-windows-static' - printf("Appended '%s' to '%s' project", triplet, prj.name) - premake.w(triplet) - end -end - -premake.override(premake.vstudio.vc2010.elements, "project", function(base, prj) - local calls = base(prj) - table.insertafter(calls, vs.xmlDeclaration, premakeVersionComment) - return calls -end) - -premake.override(premake.vstudio.vc2010.elements, "globals", function(base, prj) - local calls = base(prj) - table.insertafter(calls, vs.globals, vcpkg) - return calls -end) +include "./vendor/premake/premake_customization/solution_items.lua" -- workspace is the solution workspace "pyro" architecture "x64" - startproject "ember" + startproject "sandbox" configurations { @@ -44,15 +17,17 @@ outputdir = "%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}" -- include directories relative to root folder (sln dir) IncludeDir = {} -IncludeDir["GLFW"] = "pyro/external/GLFW/include" -IncludeDir["Glad"] = "pyro/external/Glad/include" -IncludeDir["glm"] = "pyro/external/glm" -IncludeDir["stb_image"] = "pyro/external/stb_image" -IncludeDir["ImGui"] = "pyro/external/imgui" -IncludeDir["assimp"] = "pyro/external/assimp/include/" -IncludeDir["assimpcfg"] = "pyro/external/assimp/config/" +IncludeDir["GLFW"] = "%{wks.location}/pyro/external/GLFW/include" +IncludeDir["Glad"] = "%{wks.location}/pyro/external/Glad/include" +IncludeDir["glm"] = "%{wks.location}/pyro/external/glm" +IncludeDir["stb_image"] = "%{wks.location}/pyro/external/stb_image" +IncludeDir["ImGui"] = "%{wks.location}/pyro/external/imgui" +IncludeDir["entt"] = "%{wks.location}/pyro/external/entt/include" +IncludeDir["assimp"] = "%{wks.location}/pyro/external/assimp/include/" +IncludeDir["assimpcfg"] = "%{wks.location}/pyro/external/assimp/config/" group "dependencies" + include "vendor/premake" include "pyro/external/GLFW" include "pyro/external/Glad" include "pyro/external/imgui" @@ -61,179 +36,10 @@ group "dependencies" group "" -- engine core project -project "pyro" - -- location makes sure that everything below will be relative to the project directory - location "pyro" - kind "StaticLib" -- Static library (.lib) - language "C++" - cppdialect "C++17" - staticruntime "on" - - targetdir ("bin/" .. outputdir .. "/%{prj.name}") - objdir ("inter/" .. outputdir .. "/%{prj.name}") - - pchheader "pyro_pch.h" - pchsource "pyro/src/pyro_pch.cpp" - - files - { - -- ** means recursively search down that folder - "%{prj.name}/src/**.h", - "%{prj.name}/src/**.cpp", - "%{prj.name}/external/glm/glm/**.hpp", - "%{prj.name}/external/glm/glm/**.inl", - } - - defines - { - "_CRT_SECURE_NO_WARNINGS", - "GLFW_INCLUDE_NONE" - } - - includedirs - { - "%{prj.name}/src", - "%{prj.name}/external/spdlog/include", - "%{IncludeDir.GLFW}", - "%{IncludeDir.Glad}", - "%{IncludeDir.ImGui}", - "%{IncludeDir.glm}", - "%{IncludeDir.stb_image}", - } - - links - { - "GLFW", - "Glad", - "ImGui", - "opengl32.lib", - "stb_image", - } - - -- filters are used to apply property to some specific configurations only - filter "system:windows" - systemversion "latest" -- windows SDK version - - filter "configurations:Debug" - defines "PYRO_DEBUG" - runtime "Debug" - symbols "on" - - filter "configurations:Release" - defines "PYRO_RELEASE" - runtime "Release" - optimize "on" - - filter "configurations:Dist" - defines "PYRO_DIST" - runtime "Release" - optimize "on" - +include "pyro" -- ember application -project "ember" - location "ember" - kind "ConsoleApp" - language "C++" - cppdialect "C++17" - staticruntime "on" - - targetdir ("bin/" .. outputdir .. "/%{prj.name}") - objdir ("inter/" .. outputdir .. "/%{prj.name}") - - files - { - "%{prj.name}/src/**.h", - "%{prj.name}/src/**.cpp" - } - - includedirs - { - "%{prj.name}/src/", - "pyro/external/spdlog/include", - "pyro/src", - "pyro/external", - "%{IncludeDir.glm}", - "%{IncludeDir.ImGui}", - } - - links - { - "pyro" - } - - filter "system:windows" - systemversion "latest" - - filter "configurations:Debug" - defines "PYRO_DEBUG" - runtime "Debug" - symbols "on" - - filter "configurations:Release" - defines "PYRO_RELEASE" - runtime "Release" - optimize "on" - - filter "configurations:Dist" - defines "PYRO_DIST" - runtime "Release" - optimize "on" - --- include "tests" +include "ember" +-- sandbox application +include "sandbox" -- Test application -project "tests" - location "tests" - - kind "ConsoleApp" - language "C++" - cppdialect "C++17" - staticruntime "on" - - targetdir ("bin/" .. outputdir .. "/%{prj.name}") - objdir ("inter/" .. outputdir .. "/%{prj.name}") - - files - { - "%{prj.name}/src/**.h", - "%{prj.name}/src/**.cpp" - } - - includedirs - { - "%{prj.name}/src/", - "ember/src", - "pyro/external/spdlog/include", - "pyro/src", - "pyro/external", - "%{IncludeDir.glm}", - "%{IncludeDir.ImGui}", - } - - links - { - "ember", - "pyro", - } - - filter "system:windows" - systemversion "latest" - - defines - { - "NOMINMAX", - } - - filter "configurations:Debug" - defines "PYRO_DEBUG" - runtime "Debug" - symbols "on" - - filter "configurations:Release" - defines "PYRO_RELEASE" - runtime "Release" - optimize "on" - - filter "configurations:Dist" - defines "PYRO_DIST" - runtime "Release" - optimize "on" +include "tests" \ No newline at end of file diff --git a/pyro/external/GLFW b/pyro/external/GLFW index 0156dfc..40e5453 160000 --- a/pyro/external/GLFW +++ b/pyro/external/GLFW @@ -1 +1 @@ -Subproject commit 0156dfcc8de511c7710eb20f427b3f82f98963ab +Subproject commit 40e5453761a24bc1b0eb2acc25743a37ef1961b1 diff --git a/pyro/external/Glad/premake5.lua b/pyro/external/Glad/premake5.lua index e109401..5b9a7ac 100644 --- a/pyro/external/Glad/premake5.lua +++ b/pyro/external/Glad/premake5.lua @@ -2,9 +2,9 @@ project "Glad" kind "StaticLib" language "C" staticruntime "on" - - targetdir ("bin/" .. outputdir .. "/%{prj.name}") - objdir ("inter/" .. outputdir .. "/%{prj.name}") + + targetdir ("%{wks.location}/bin/" .. outputdir .. "/%{prj.name}") + objdir ("%{wks.location}/inter/" .. outputdir .. "/%{prj.name}") files { @@ -17,7 +17,7 @@ project "Glad" { "include" } - + filter "system:windows" systemversion "latest" diff --git a/pyro/external/assimp b/pyro/external/assimp index b4f743a..1eb2f58 160000 --- a/pyro/external/assimp +++ b/pyro/external/assimp @@ -1 +1 @@ -Subproject commit b4f743aed5cce7491522ea1ac6d2cf8722baf20a +Subproject commit 1eb2f58b8d952f475152f66e853f4e122f17219f diff --git a/pyro/external/entt/LICENSE.txt b/pyro/external/entt/LICENSE.txt new file mode 100644 index 0000000..1c5f480 --- /dev/null +++ b/pyro/external/entt/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2020 Michele Caini + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copy of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copy or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pyro/external/entt/include/entt.hpp b/pyro/external/entt/include/entt.hpp new file mode 100644 index 0000000..2ecceb9 --- /dev/null +++ b/pyro/external/entt/include/entt.hpp @@ -0,0 +1,17599 @@ +// #include "core/algorithm.hpp" +#ifndef ENTT_CORE_ALGORITHM_HPP +#define ENTT_CORE_ALGORITHM_HPP + + +#include +#include +#include +#include +#include +// #include "utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + + +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty_v +#else +# include +# // sfinae-friendly definition +# define ENTT_IS_EMPTY(Type) (false && std::is_empty_v) +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) __VA_ARGS__ +# endif +#endif + + +#endif + + + +namespace entt { + + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + constexpr Type && operator()(Type &&value) const ENTT_NOEXCEPT { + return std::forward(value); + } +}; + + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +constexpr auto overload(Type Class:: *member) ENTT_NOEXCEPT { return member; } + + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +constexpr auto overload(Func *func) ENTT_NOEXCEPT { return func; } + + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive): + func{std::move(recursive)} + {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&... args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&... args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Function object to wrap `std::sort` in a class type. + * + * Unfortunately, `std::sort` cannot be passed as template argument to a class + * template or a function template.
+ * This class fills the gap by wrapping some flavors of `std::sort` in a + * function object. + */ +struct std_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @tparam Args Types of arguments to forward to the sort function. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param args Arguments to forward to the sort function, if any. + */ + template, typename... Args> + void operator()(It first, It last, Compare compare = Compare{}, Args &&... args) const { + std::sort(std::forward(args)..., std::move(first), std::move(last), std::move(compare)); + } +}; + + +/*! @brief Function object for performing insertion sort. */ +struct insertion_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + */ + template> + void operator()(It first, It last, Compare compare = Compare{}) const { + if(first < last) { + for(auto it = first+1; it < last; ++it) { + auto value = std::move(*it); + auto pre = it; + + for(; pre > first && compare(value, *(pre-1)); --pre) { + *pre = std::move(*(pre-1)); + } + + *pre = std::move(value); + } + } + } +}; + + +/** + * @brief Function object for performing LSD radix sort. + * @tparam Bit Number of bits processed per pass. + * @tparam N Maximum number of bits to sort. + */ +template +struct radix_sort { + static_assert((N % Bit) == 0); + + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given _getter_ to access the + * actual data to be sorted. + * + * This implementation is inspired by the online book + * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). + * + * @tparam It Type of random access iterator. + * @tparam Getter Type of _getter_ function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param getter A valid _getter_ function object. + */ + template + void operator()(It first, It last, Getter getter = Getter{}) const { + if(first < last) { + static constexpr auto mask = (1 << Bit) - 1; + static constexpr auto buckets = 1 << Bit; + static constexpr auto passes = N / Bit; + + using value_type = typename std::iterator_traits::value_type; + std::vector aux(std::distance(first, last)); + + auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { + std::size_t index[buckets]{}; + std::size_t count[buckets]{}; + + std::for_each(from, to, [&getter, &count, start](const value_type &item) { + ++count[(getter(item) >> start) & mask]; + }); + + std::for_each(std::next(std::begin(index)), std::end(index), [index = std::begin(index), count = std::begin(count)](auto &item) mutable { + item = *(index++) + *(count++); + }); + + std::for_each(from, to, [&getter, &out, &index, start](value_type &item) { + out[index[(getter(item) >> start) & mask]++] = std::move(item); + }); + }; + + for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) { + part(first, last, aux.begin(), pass * Bit); + part(aux.begin(), aux.end(), first, (pass + 1) * Bit); + } + + if constexpr(passes & 1) { + part(first, last, aux.begin(), (passes - 1) * Bit); + std::move(aux.begin(), aux.end(), first); + } + } + } +}; + + +} + + +#endif + +// #include "core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + + +#endif + +// #include "core/family.hpp" +#ifndef ENTT_CORE_FAMILY_HPP +#define ENTT_CORE_FAMILY_HPP + + +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +// #include "../config/config.h" + + + +namespace entt { + + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Dynamic identifier generator. + * + * Utility class template that can be used to assign unique identifiers to types + * at runtime. Use different specializations to create separate sets of + * identifiers. + */ +template +class family { + inline static ENTT_MAYBE_ATOMIC(id_type) identifier{}; + +public: + /*! @brief Unsigned integer type. */ + using family_type = id_type; + + /*! @brief Statically generated unique identifier for the given type. */ + template + // at the time I'm writing, clang crashes during compilation if auto is used instead of family_type + inline static const family_type type = identifier++; +}; + + +} + + +#endif + +// #include "core/hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct fnv1a_traits; + + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string { + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {} + const Char *str; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT { + auto value = traits_type::offset; + + while(*curr != 0) { + value = (value ^ static_cast(*(curr++))) * traits_type::prime; + } + + return value; + } + +public: + /*! @brief Character type. */ + using value_type = Char; + /*! @brief Unsigned integer type. */ + using hash_type = id_type; + + /** + * @brief Returns directly the numeric representation of a string. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * const auto value = basic_hashed_string::to_value("my.png"); + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + * @return The numeric representation of the string. + */ + template + static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); + } + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_NOEXCEPT + : str{nullptr}, hash{} + {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * basic_hashed_string hs{"my.png"}; + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param curr Human-readable identifer. + */ + template + constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT + : str{curr}, hash{helper(curr)} + {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + : str{wrapper.str}, hash{helper(wrapper.str)} + {} + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the instance. + */ + constexpr const value_type * data() const ENTT_NOEXCEPT { + return str; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + constexpr hash_type value() const ENTT_NOEXCEPT { + return hash; + } + + /*! @copydoc data */ + constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } + + /** + * @brief Compares two hashed strings. + * @param other Hashed string with which to compare. + * @return True if the two hashed strings are identical, false otherwise. + */ + constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + return hash == other.hash; + } + +private: + const value_type *str; + hash_type hash; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the character type of the hashed string directly from a + * human-readable identifer provided to the constructor. + * + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + */ +template +basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT +-> basic_hashed_string; + + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template +constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + + +} + + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_string{str}; +} + + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_wstring{str}; +} + + +#endif + +// #include "core/ident.hpp" +#ifndef ENTT_CORE_IDENT_HPP +#define ENTT_CORE_IDENT_HPP + + +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Types identifiers. + * + * Variable template used to generate identifiers at compile-time for the given + * types. Use the `get` member function to know what's the identifier associated + * to the specific type. + * + * @note + * Identifiers are constant expression and can be used in any context where such + * an expression is required. As an example: + * @code{.cpp} + * using id = entt::identifier; + * + * switch(a_type_identifier) { + * case id::type: + * // ... + * break; + * case id::type: + * // ... + * break; + * default: + * // ... + * } + * @endcode + * + * @tparam Types List of types for which to generate identifiers. + */ +template +class identifier { + using tuple_type = std::tuple...>; + + template + static constexpr id_type get(std::index_sequence) { + static_assert(std::disjunction_v...>); + return (0 + ... + (std::is_same_v> ? id_type(Indexes) : id_type{})); + } + +public: + /*! @brief Unsigned integer type. */ + using identifier_type = id_type; + + /*! @brief Statically generated unique identifier for the given type. */ + template + static constexpr identifier_type type = get>(std::index_sequence_for{}); +}; + + +} + + +#endif + +// #include "core/monostate.hpp" +#ifndef ENTT_CORE_MONOSTATE_HPP +#define ENTT_CORE_MONOSTATE_HPP + + +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Minimal implementation of the monostate pattern. + * + * A minimal, yet complete configuration system built on top of the monostate + * pattern. Thread safe by design, it works only with basic types like `int`s or + * `bool`s.
+ * Multiple types and therefore more than one value can be associated with a + * single key. Because of this, users must pay attention to use the same type + * both during an assignment and when they try to read back their data. + * Otherwise, they can incur in unexpected results. + */ +template +struct monostate { + /** + * @brief Assigns a value of a specific type to a given key. + * @tparam Type Type of the value to assign. + * @param val User data to assign to the given key. + */ + template + void operator=(Type val) const ENTT_NOEXCEPT { + value = val; + } + + /** + * @brief Gets a value of a specific type for a given key. + * @tparam Type Type of the value to get. + * @return Stored value, if any. + */ + template + operator Type() const ENTT_NOEXCEPT { + return value; + } + +private: + template + inline static ENTT_MAYBE_ATOMIC(Type) value{}; +}; + + +/** + * @brief Helper variable template. + * @tparam Value Value used to differentiate between different variables. + */ +template +inline monostate monostate_v = {}; + + +} + + +#endif + +// #include "core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + + +#endif + +// #include "hashed_string.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_index { + static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/** + * @brief Type index. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } +}; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * indexable, false otherwise. + * @tparam Type Potentially indexable type. + */ +template +struct has_type_index: std::false_type {}; + + +/*! @brief has_type_index */ +template +struct has_type_index::value())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially indexable type. + */ +template +inline constexpr bool has_type_index_v = has_type_index::value; + + +/** + * @brief Type info. + * @tparam Type Type for which to generate information. + */ +template +struct ENTT_API type_info { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + static ENTT_PRETTY_FUNCTION_CONSTEXPR() id_type id() ENTT_NOEXCEPT { + ENTT_PRETTY_FUNCTION_CONSTEXPR(static const) auto value = entt::hashed_string::value(ENTT_PRETTY_FUNCTION); + return value; + } +#else + static id_type id() ENTT_NOEXCEPT { + return type_index::value(); + } +#endif +}; + + +} + + +#endif + +// #include "core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "hashed_string.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to ease the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond TURN_OFF_DOXYGEN */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + + +/*! @brief A class to use to push around lists of types, nothing more. */ +template +struct type_list {}; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_size; + + +/** + * @brief Compile-time number of elements in a type list. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_size> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + */ +template +inline constexpr auto type_list_size_v = type_list_size::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template> +struct is_equality_comparable: std::false_type {}; + + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + + +/** + * @brief Defines an enum class to use for opaque identifiers and a dedicate + * `to_integer` function to convert the identifiers to their underlying type. + * @param clazz The name to use for the enum class. + * @param type The underlying type for the enum class. + */ +#define ENTT_OPAQUE_TYPE(clazz, type)\ + enum class clazz: type {};\ + constexpr auto to_integral(const clazz id) ENTT_NOEXCEPT {\ + return static_cast>(id);\ + }\ + static_assert(true) + + +#endif + +// #include "core/utility.hpp" + +// #include "entity/actor.hpp" +#ifndef ENTT_ENTITY_ACTOR_HPP +#define ENTT_ENTITY_ACTOR_HPP + + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty_v +#else +# include +# // sfinae-friendly definition +# define ENTT_IS_EMPTY(Type) (false && std::is_empty_v) +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) __VA_ARGS__ +# endif +#endif + + +#endif + +// #include "registry.hpp" +#ifndef ENTT_ENTITY_REGISTRY_HPP +#define ENTT_ENTITY_REGISTRY_HPP + + +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" +#ifndef ENTT_CORE_ALGORITHM_HPP +#define ENTT_CORE_ALGORITHM_HPP + + +#include +#include +#include +#include +#include +// #include "utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + + +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty_v +#else +# include +# // sfinae-friendly definition +# define ENTT_IS_EMPTY(Type) (false && std::is_empty_v) +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) __VA_ARGS__ +# endif +#endif + + +#endif + + + +namespace entt { + + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + constexpr Type && operator()(Type &&value) const ENTT_NOEXCEPT { + return std::forward(value); + } +}; + + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +constexpr auto overload(Type Class:: *member) ENTT_NOEXCEPT { return member; } + + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +constexpr auto overload(Func *func) ENTT_NOEXCEPT { return func; } + + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive): + func{std::move(recursive)} + {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&... args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&... args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Function object to wrap `std::sort` in a class type. + * + * Unfortunately, `std::sort` cannot be passed as template argument to a class + * template or a function template.
+ * This class fills the gap by wrapping some flavors of `std::sort` in a + * function object. + */ +struct std_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @tparam Args Types of arguments to forward to the sort function. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param args Arguments to forward to the sort function, if any. + */ + template, typename... Args> + void operator()(It first, It last, Compare compare = Compare{}, Args &&... args) const { + std::sort(std::forward(args)..., std::move(first), std::move(last), std::move(compare)); + } +}; + + +/*! @brief Function object for performing insertion sort. */ +struct insertion_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + */ + template> + void operator()(It first, It last, Compare compare = Compare{}) const { + if(first < last) { + for(auto it = first+1; it < last; ++it) { + auto value = std::move(*it); + auto pre = it; + + for(; pre > first && compare(value, *(pre-1)); --pre) { + *pre = std::move(*(pre-1)); + } + + *pre = std::move(value); + } + } + } +}; + + +/** + * @brief Function object for performing LSD radix sort. + * @tparam Bit Number of bits processed per pass. + * @tparam N Maximum number of bits to sort. + */ +template +struct radix_sort { + static_assert((N % Bit) == 0); + + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given _getter_ to access the + * actual data to be sorted. + * + * This implementation is inspired by the online book + * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). + * + * @tparam It Type of random access iterator. + * @tparam Getter Type of _getter_ function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param getter A valid _getter_ function object. + */ + template + void operator()(It first, It last, Getter getter = Getter{}) const { + if(first < last) { + static constexpr auto mask = (1 << Bit) - 1; + static constexpr auto buckets = 1 << Bit; + static constexpr auto passes = N / Bit; + + using value_type = typename std::iterator_traits::value_type; + std::vector aux(std::distance(first, last)); + + auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { + std::size_t index[buckets]{}; + std::size_t count[buckets]{}; + + std::for_each(from, to, [&getter, &count, start](const value_type &item) { + ++count[(getter(item) >> start) & mask]; + }); + + std::for_each(std::next(std::begin(index)), std::end(index), [index = std::begin(index), count = std::begin(count)](auto &item) mutable { + item = *(index++) + *(count++); + }); + + std::for_each(from, to, [&getter, &out, &index, start](value_type &item) { + out[index[(getter(item) >> start) & mask]++] = std::move(item); + }); + }; + + for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) { + part(first, last, aux.begin(), pass * Bit); + part(aux.begin(), aux.end(), first, (pass + 1) * Bit); + } + + if constexpr(passes & 1) { + part(first, last, aux.begin(), (passes - 1) * Bit); + std::move(aux.begin(), aux.end(), first); + } + } + } +}; + + +} + + +#endif + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +// #include "../config/config.h" + + + +namespace entt { + + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + + +} + + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + + +#endif + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct fnv1a_traits; + + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string { + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {} + const Char *str; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT { + auto value = traits_type::offset; + + while(*curr != 0) { + value = (value ^ static_cast(*(curr++))) * traits_type::prime; + } + + return value; + } + +public: + /*! @brief Character type. */ + using value_type = Char; + /*! @brief Unsigned integer type. */ + using hash_type = id_type; + + /** + * @brief Returns directly the numeric representation of a string. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * const auto value = basic_hashed_string::to_value("my.png"); + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + * @return The numeric representation of the string. + */ + template + static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); + } + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_NOEXCEPT + : str{nullptr}, hash{} + {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * basic_hashed_string hs{"my.png"}; + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param curr Human-readable identifer. + */ + template + constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT + : str{curr}, hash{helper(curr)} + {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + : str{wrapper.str}, hash{helper(wrapper.str)} + {} + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the instance. + */ + constexpr const value_type * data() const ENTT_NOEXCEPT { + return str; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + constexpr hash_type value() const ENTT_NOEXCEPT { + return hash; + } + + /*! @copydoc data */ + constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } + + /** + * @brief Compares two hashed strings. + * @param other Hashed string with which to compare. + * @return True if the two hashed strings are identical, false otherwise. + */ + constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + return hash == other.hash; + } + +private: + const value_type *str; + hash_type hash; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the character type of the hashed string directly from a + * human-readable identifer provided to the constructor. + * + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + */ +template +basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT +-> basic_hashed_string; + + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template +constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + + +} + + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_string{str}; +} + + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_wstring{str}; +} + + +#endif + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_index { + static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/** + * @brief Type index. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } +}; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * indexable, false otherwise. + * @tparam Type Potentially indexable type. + */ +template +struct has_type_index: std::false_type {}; + + +/*! @brief has_type_index */ +template +struct has_type_index::value())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially indexable type. + */ +template +inline constexpr bool has_type_index_v = has_type_index::value; + + +/** + * @brief Type info. + * @tparam Type Type for which to generate information. + */ +template +struct ENTT_API type_info { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + static ENTT_PRETTY_FUNCTION_CONSTEXPR() id_type id() ENTT_NOEXCEPT { + ENTT_PRETTY_FUNCTION_CONSTEXPR(static const) auto value = entt::hashed_string::value(ENTT_PRETTY_FUNCTION); + return value; + } +#else + static id_type id() ENTT_NOEXCEPT { + return type_index::value(); + } +#endif +}; + + +} + + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "hashed_string.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to ease the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond TURN_OFF_DOXYGEN */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + + +/*! @brief A class to use to push around lists of types, nothing more. */ +template +struct type_list {}; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_size; + + +/** + * @brief Compile-time number of elements in a type list. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_size> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + */ +template +inline constexpr auto type_list_size_v = type_list_size::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template> +struct is_equality_comparable: std::false_type {}; + + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + + +/** + * @brief Defines an enum class to use for opaque identifiers and a dedicate + * `to_integer` function to convert the identifiers to their underlying type. + * @param clazz The name to use for the enum class. + * @param type The underlying type for the enum class. + */ +#define ENTT_OPAQUE_TYPE(clazz, type)\ + enum class clazz: type {};\ + constexpr auto to_integral(const clazz id) ENTT_NOEXCEPT {\ + return static_cast>(id);\ + }\ + static_assert(true) + + +#endif + +// #include "../signal/sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty_v +#else +# include +# // sfinae-friendly definition +# define ENTT_IS_EMPTY(Type) (false && std::is_empty_v) +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) __VA_ARGS__ +# endif +#endif + + +#endif + +// #include "delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + + +#include +#include +#include +#include +#include +// #include "../config/config.h" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +auto function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(*)(Type, Args...), Other &&) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(Class:: *)(Args...), Other &&...) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(Class:: *)(Args...) const, Other &&...) -> Ret(*)(Args...); + + +template +auto function_pointer(Type Class:: *, Other &&...) -> Type(*)(); + + +template +using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); + + +template +constexpr auto index_sequence_for(Ret(*)(Args...)) { + return std::index_sequence_for{}; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/*! @brief Used to wrap a function or a member of a specified type. */ +template +struct connect_arg_t {}; + + +/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ +template +inline constexpr connect_arg_t connect_arg{}; + + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template +class delegate; + + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class delegate { + using proto_fn_type = Ret(const void *, Args...); + + template + auto wrap(std::index_sequence) ENTT_NOEXCEPT { + return [](const void *, Args... args) -> Ret { + const auto arguments = std::forward_as_tuple(std::forward(args)...); + return Ret(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + }; + } + + template + auto wrap(Type &, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + }; + } + + template + auto wrap(Type *, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + }; + } + +public: + /*! @brief Function type of the delegate. */ + using function_type = Ret(Args...); + + /*! @brief Default constructor. */ + delegate() ENTT_NOEXCEPT + : fn{nullptr}, data{nullptr} + {} + + /** + * @brief Constructs a delegate and connects a free function or an unbound + * member. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + delegate(connect_arg_t) ENTT_NOEXCEPT + : delegate{} + { + connect(); + } + + /** + * @brief Constructs a delegate and connects a free function with payload or + * a bound member. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT + : delegate{} + { + connect(std::forward(value_or_instance)); + } + + /** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + void connect() ENTT_NOEXCEPT { + data = nullptr; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *, Args... args) -> Ret { + return Ret(std::invoke(Candidate, std::forward(args)...)); + }; + } else if constexpr(std::is_member_pointer_v) { + fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); + } else { + fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. + */ + template + void connect(Type &value_or_instance) ENTT_NOEXCEPT { + data = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * @sa connect(Type &) + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + */ + template + void connect(Type *value_or_instance) ENTT_NOEXCEPT { + data = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() ENTT_NOEXCEPT { + fn = nullptr; + data = nullptr; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + const void * instance() const ENTT_NOEXCEPT { + return data; + } + + /** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * delegate has not yet been set. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ + Ret operator()(Args... args) const { + ENTT_ASSERT(fn); + return fn(data, std::forward(args)...); + } + + /** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ + explicit operator bool() const ENTT_NOEXCEPT { + // no need to test also data + return !(fn == nullptr); + } + + /** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ + bool operator==(const delegate &other) const ENTT_NOEXCEPT { + return fn == other.fn && data == other.data; + } + +private: + proto_fn_type *fn; + const void *data; +}; + + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template +bool operator!=(const delegate &lhs, const delegate &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) ENTT_NOEXCEPT +-> delegate>>; + + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template +delegate(connect_arg_t, Type &&) ENTT_NOEXCEPT +-> delegate>>; + + +} + + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_SIGNAL_FWD_HPP +#define ENTT_SIGNAL_FWD_HPP + + +namespace entt { + + +/*! @class delegate */ +template +class delegate; + +/*! @class dispatcher */ +class dispatcher; + +/*! @class emitter */ +template +class emitter; + +/*! @class connection */ +class connection; + +/*! @class scoped_connection */ +struct scoped_connection; + +/*! @class sink */ +template +class sink; + +/*! @class sigh */ +template +class sigh; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Sink class. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Function A valid function type. + */ +template +class sink; + + +/** + * @brief Unmanaged signal handler. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Function A valid function type. + */ +template +class sigh; + + +/** + * @brief Unmanaged signal handler. + * + * It works directly with references to classes and pointers to member functions + * as well as pointers to free functions. Users of this class are in charge of + * disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals to use later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class sigh { + /*! @brief A sink is allowed to modify a signal. */ + friend class sink; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Sink type. */ + using sink_type = entt::sink; + + /** + * @brief Instance type when it comes to connecting member functions. + * @tparam Class Type of class to which the member function belongs. + */ + template + using instance_type = Class *; + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + size_type size() const ENTT_NOEXCEPT { + return calls.size(); + } + + /** + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return calls.empty(); + } + + /** + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. + */ + void publish(Args... args) const { + for(auto &&call: std::as_const(calls)) { + call(args...); + } + } + + /** + * @brief Collects return values from the listeners. + * + * The collector must expose a call operator with the following properties: + * + * * The return type is either `void` or such that it's convertible to + * `bool`. In the second case, a true value will stop the iteration. + * * The list of parameters is empty if `Ret` is `void`, otherwise it + * contains a single element such that `Ret` is convertible to it. + * + * @tparam Func Type of collector to use, if any. + * @param func A valid function object. + * @param args Arguments to use to invoke listeners. + */ + template + void collect(Func func, Args... args) const { + for(auto &&call: calls) { + if constexpr(std::is_void_v) { + if constexpr(std::is_invocable_r_v) { + call(args...); + if(func()) { break; } + } else { + call(args...); + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(call(args...))) { break; } + } else { + func(call(args...)); + } + } + } + } + +private: + std::vector> calls; +}; + + +/** + * @brief Connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it. + */ +class connection { + /*! @brief A sink is allowed to create connection objects. */ + template + friend class sink; + + connection(delegate fn, void *ref) + : disconnect{fn}, signal{ref} + {} + +public: + /*! @brief Default constructor. */ + connection() = default; + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(disconnect); + } + + /*! @brief Breaks the connection. */ + void release() { + if(disconnect) { + disconnect(signal); + disconnect.reset(); + } + } + +private: + delegate disconnect; + void *signal{}; +}; + + +/** + * @brief Scoped connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it.
+ * A scoped connection automatically breaks the link between the two objects + * when it goes out of scope. + */ +struct scoped_connection { + /*! @brief Default constructor. */ + scoped_connection() = default; + + /** + * @brief Constructs a scoped connection from a basic connection. + * @param other A valid connection object. + */ + scoped_connection(const connection &other) + : conn{other} + {} + + /*! @brief Default copy constructor, deleted on purpose. */ + scoped_connection(const scoped_connection &) = delete; + + /*! @brief Automatically breaks the link on destruction. */ + ~scoped_connection() { + conn.release(); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This scoped connection. + */ + scoped_connection & operator=(const scoped_connection &) = delete; + + /** + * @brief Acquires a connection. + * @param other The connection object to acquire. + * @return This scoped connection. + */ + scoped_connection & operator=(connection other) { + conn = std::move(other); + return *this; + } + + /** + * @brief Checks whether a scoped connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(conn); + } + + /*! @brief Breaks the connection. */ + void release() { + conn.release(); + } + +private: + connection conn; +}; + + +/** + * @brief Sink class. + * + * A sink is used to connect listeners to signals and to disconnect them.
+ * The function type for a listener is the one of the signal to which it + * belongs. + * + * The clear separation between a signal and a sink permits to store the former + * as private data member without exposing the publish functionality to the + * users of the class. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class sink { + using signal_type = sigh; + using difference_type = typename std::iterator_traits::difference_type; + + template + static void release(Type value_or_instance, void *signal) { + sink{*static_cast(signal)}.disconnect(value_or_instance); + } + + template + static void release(void *signal) { + sink{*static_cast(signal)}.disconnect(); + } + +public: + /** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ + sink(sigh &ref) ENTT_NOEXCEPT + : offset{}, + signal{&ref} + {} + + /** + * @brief Returns false if at least a listener is connected to the sink. + * @return True if the sink has no listeners connected, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return signal->calls.empty(); + } + + /** + * @brief Returns a sink that connects before a given free function or an + * unbound member. + * @tparam Function A valid free function pointer. + * @return A properly initialized sink object. + */ + template + sink before() { + delegate call{}; + call.template connect(); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = std::distance(it, calls.cend()); + return other; + } + + /** + * @brief Returns a sink that connects before a free function with payload + * or a bound member. + * @tparam Candidate Member or free function to look for. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ + template + sink before(Type &&value_or_instance) { + delegate call{}; + call.template connect(std::forward(value_or_instance)); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = std::distance(it, calls.cend()); + return other; + } + + /** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ + template + sink before(Type &value_or_instance) { + return before(&value_or_instance); + } + + /** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + * @return A properly initialized sink object. + */ + template + sink before(Type *value_or_instance) { + sink other{*this}; + + if(value_or_instance) { + const auto &calls = signal->calls; + const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) { + return delegate.instance() == value_or_instance; + }); + + other.offset = std::distance(it, calls.cend()); + } + + return other; + } + + /** + * @brief Returns a sink that connects before anything else. + * @return A properly initialized sink object. + */ + sink before() { + sink other{*this}; + other.offset = signal->calls.size(); + return other; + } + + /** + * @brief Connects a free function or an unbound member to a signal. + * + * The signal handler performs checks to avoid multiple connections for the + * same function. + * + * @tparam Candidate Function or member to connect to the signal. + * @return A properly initialized connection object. + */ + template + connection connect() { + disconnect(); + + delegate call{}; + call.template connect(); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(); + return { std::move(conn), signal }; + } + + /** + * @brief Connects a free function with payload or a bound member to a + * signal. + * + * The signal isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the signal. On the other side, the signal handler performs + * checks to avoid multiple connections for the same function.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the signal itself. + * + * @tparam Candidate Function or member to connect to the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized connection object. + */ + template + connection connect(Type &&value_or_instance) { + disconnect(value_or_instance); + + delegate call{}; + call.template connect(value_or_instance); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance); + return { std::move(conn), signal }; + } + + /** + * @brief Disconnects a free function or an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + */ + template + void disconnect() { + auto &calls = signal->calls; + delegate call{}; + call.template connect(); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); + } + + /** + * @brief Disconnects a free function with payload or a bound member from a + * signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &&value_or_instance) { + auto &calls = signal->calls; + delegate call{}; + call.template connect(std::forward(value_or_instance)); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &value_or_instance) { + disconnect(&value_or_instance); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type *value_or_instance) { + if(value_or_instance) { + auto &calls = signal->calls; + calls.erase(std::remove_if(calls.begin(), calls.end(), [value_or_instance](const auto &delegate) { + return delegate.instance() == value_or_instance; + }), calls.end()); + } + } + + /*! @brief Disconnects all the listeners from a signal. */ + void disconnect() { + signal->calls.clear(); + } + +private: + difference_type offset; + signal_type *signal; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the function type of a sink directly from the signal it + * refers to. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +sink(sigh &) ENTT_NOEXCEPT -> sink; + + +} + + +#endif + +// #include "entity.hpp" +#ifndef ENTT_ENTITY_ENTITY_HPP +#define ENTT_ENTITY_ENTITY_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "../core/fwd.hpp" + + + +namespace entt { + + +/** + * @brief Entity traits. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is an accepted entity type. + */ +template +struct entt_traits; + + +/** + * @brief Entity traits for a 16 bits entity identifier. + * + * A 16 bits entity identifier guarantees: + * + * * 12 bits for the entity number (up to 4k entities). + * * 4 bit for the version (resets in [0-15]). + */ +template<> +struct entt_traits { + /*! @brief Underlying entity type. */ + using entity_type = std::uint16_t; + /*! @brief Underlying version type. */ + using version_type = std::uint8_t; + /*! @brief Difference type. */ + using difference_type = std::int32_t; + + /*! @brief Mask to use to get the entity number out of an identifier. */ + static constexpr std::uint16_t entity_mask = 0xFFF; + /*! @brief Mask to use to get the version out of an identifier. */ + static constexpr std::uint16_t version_mask = 0xF; + /*! @brief Extent of the entity number within an identifier. */ + static constexpr auto entity_shift = 12; +}; + + +/** + * @brief Entity traits for a 32 bits entity identifier. + * + * A 32 bits entity identifier guarantees: + * + * * 20 bits for the entity number (suitable for almost all the games). + * * 12 bit for the version (resets in [0-4095]). + */ +template<> +struct entt_traits { + /*! @brief Underlying entity type. */ + using entity_type = std::uint32_t; + /*! @brief Underlying version type. */ + using version_type = std::uint16_t; + /*! @brief Difference type. */ + using difference_type = std::int64_t; + + /*! @brief Mask to use to get the entity number out of an identifier. */ + static constexpr std::uint32_t entity_mask = 0xFFFFF; + /*! @brief Mask to use to get the version out of an identifier. */ + static constexpr std::uint32_t version_mask = 0xFFF; + /*! @brief Extent of the entity number within an identifier. */ + static constexpr auto entity_shift = 20; +}; + + +/** + * @brief Entity traits for a 64 bits entity identifier. + * + * A 64 bits entity identifier guarantees: + * + * * 32 bits for the entity number (an indecently large number). + * * 32 bit for the version (an indecently large number). + */ +template<> +struct entt_traits { + /*! @brief Underlying entity type. */ + using entity_type = std::uint64_t; + /*! @brief Underlying version type. */ + using version_type = std::uint32_t; + /*! @brief Difference type. */ + using difference_type = std::int64_t; + + /*! @brief Mask to use to get the entity number out of an identifier. */ + static constexpr std::uint64_t entity_mask = 0xFFFFFFFF; + /*! @brief Mask to use to get the version out of an identifier. */ + static constexpr std::uint64_t version_mask = 0xFFFFFFFF; + /*! @brief Extent of the entity number within an identifier. */ + static constexpr auto entity_shift = 32; +}; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +class null { + template + using traits_type = entt_traits>; + +public: + template + constexpr operator Entity() const ENTT_NOEXCEPT { + return Entity{traits_type::entity_mask}; + } + + constexpr bool operator==(null) const ENTT_NOEXCEPT { + return true; + } + + constexpr bool operator!=(null) const ENTT_NOEXCEPT { + return false; + } + + template + constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { + return (to_integral(entity) & traits_type::entity_mask) == to_integral(static_cast(*this)); + } + + template + constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { + return !(entity == *this); + } +}; + + +template +constexpr bool operator==(const Entity entity, null other) ENTT_NOEXCEPT { + return other.operator==(entity); +} + + +template +constexpr bool operator!=(const Entity entity, null other) ENTT_NOEXCEPT { + return !(other == entity); +} + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/*! @brief Default entity identifier. */ +ENTT_OPAQUE_TYPE(entity, id_type); + + +/** + * @brief Compile-time constant for null entities. + * + * There exist implicit conversions from this variable to entity identifiers of + * any allowed type. Similarly, there exist comparision operators between the + * null entity and any other entity identifier. + */ +inline constexpr auto null = internal::null{}; + + +} + + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_ENTITY_FWD_HPP +#define ENTT_ENTITY_FWD_HPP + + +// #include "../core/fwd.hpp" + + + +namespace entt { + + +/*! @class basic_registry */ +template +class basic_registry; + +/*! @class basic_view */ +template +class basic_view; + +/*! @class basic_runtime_view */ +template +class basic_runtime_view; + +/*! @class basic_group */ +template +class basic_group; + +/*! @class basic_observer */ +template +class basic_observer; + +/*! @struct basic_actor */ +template +struct basic_actor; + +/*! @class basic_snapshot */ +template +class basic_snapshot; + +/*! @class basic_snapshot_loader */ +template +class basic_snapshot_loader; + +/*! @class basic_continuous_loader */ +template +class basic_continuous_loader; + +/*! @class entity */ +enum class entity: id_type; + +/*! @brief Alias declaration for the most common use case. */ +using registry = basic_registry; + +/*! @brief Alias declaration for the most common use case. */ +using observer = basic_observer; + +/*! @brief Alias declaration for the most common use case. */ +using actor = basic_actor; + +/*! @brief Alias declaration for the most common use case. */ +using snapshot = basic_snapshot; + +/*! @brief Alias declaration for the most common use case. */ +using snapshot_loader = basic_snapshot_loader; + +/*! @brief Alias declaration for the most common use case. */ +using continuous_loader = basic_continuous_loader; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Types Types of components iterated by the view. + */ +template +using view = basic_view; + +/*! @brief Alias declaration for the most common use case. */ +using runtime_view = basic_runtime_view; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Types Types of components iterated by the group. + */ +template +using group = basic_group; + + +} + + +#endif + +// #include "group.hpp" +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "sparse_set.hpp" +#ifndef ENTT_ENTITY_SPARSE_SET_HPP +#define ENTT_ENTITY_SPARSE_SET_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Basic sparse set implementation. + * + * Sparse set or packed array or whatever is the name users give it.
+ * Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a + * _packed_ one; one used for direct access through contiguous memory, the other + * one used to get the data through an extra level of indirection.
+ * This is largely used by the registry to offer users the fastest access ever + * to the components. Views and groups in general are almost entirely designed + * around sparse sets. + * + * This type of data structure is widely documented in the literature and on the + * web. This is nothing more than a customized implementation suitable for the + * purpose of the framework. + * + * @note + * There are no guarantees that entities are returned in the insertion order + * when iterate a sparse set. Do not make assumption on the order in any case. + * + * @note + * Internal data structures arrange elements to maximize performance. Because of + * that, there are no guarantees that elements have the expected order when + * iterate directly the internal packed array (see `data` and `size` member + * functions for that). Use `begin` and `end` instead. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class sparse_set { + static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0)); + static constexpr auto entt_per_page = ENTT_PAGE_SIZE / sizeof(Entity); + + using traits_type = entt_traits>; + using page_type = std::unique_ptr; + + class sparse_set_iterator final { + friend class sparse_set; + + using packed_type = std::vector; + using index_type = typename traits_type::difference_type; + + sparse_set_iterator(const packed_type &ref, const index_type idx) ENTT_NOEXCEPT + : packed{&ref}, index{idx} + {} + + public: + using difference_type = index_type; + using value_type = Entity; + using pointer = const value_type *; + using reference = const value_type &; + using iterator_category = std::random_access_iterator_tag; + + sparse_set_iterator() ENTT_NOEXCEPT = default; + + sparse_set_iterator & operator++() ENTT_NOEXCEPT { + return --index, *this; + } + + sparse_set_iterator operator++(int) ENTT_NOEXCEPT { + iterator orig = *this; + return operator++(), orig; + } + + sparse_set_iterator & operator--() ENTT_NOEXCEPT { + return ++index, *this; + } + + sparse_set_iterator operator--(int) ENTT_NOEXCEPT { + sparse_set_iterator orig = *this; + return operator--(), orig; + } + + sparse_set_iterator & operator+=(const difference_type value) ENTT_NOEXCEPT { + index -= value; + return *this; + } + + sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + sparse_set_iterator copy = *this; + return (copy += value); + } + + sparse_set_iterator & operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + difference_type operator-(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return other.index - index; + } + + reference operator[](const difference_type value) const { + const auto pos = size_type(index-value-1); + return (*packed)[pos]; + } + + bool operator==(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return other.index == index; + } + + bool operator!=(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + bool operator<(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return index > other.index; + } + + bool operator>(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return index < other.index; + } + + bool operator<=(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return !(*this > other); + } + + bool operator>=(const sparse_set_iterator &other) const ENTT_NOEXCEPT { + return !(*this < other); + } + + pointer operator->() const { + const auto pos = size_type(index-1); + return &(*packed)[pos]; + } + + reference operator*() const { + return *operator->(); + } + + private: + const packed_type *packed; + index_type index; + }; + + auto page(const Entity entt) const ENTT_NOEXCEPT { + return std::size_t{(to_integral(entt) & traits_type::entity_mask) / entt_per_page}; + } + + auto offset(const Entity entt) const ENTT_NOEXCEPT { + return std::size_t{to_integral(entt) & (entt_per_page - 1)}; + } + + page_type & assure(const std::size_t pos) { + if(!(pos < sparse.size())) { + sparse.resize(pos+1); + } + + if(!sparse[pos]) { + sparse[pos] = std::make_unique(entt_per_page); + // null is safe in all cases for our purposes + for(auto *first = sparse[pos].get(), *last = first + entt_per_page; first != last; ++first) { + *first = null; + } + } + + return sparse[pos]; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = sparse_set_iterator; + + /*! @brief Default constructor. */ + sparse_set() = default; + + /*! @brief Default move constructor. */ + sparse_set(sparse_set &&) = default; + + /*! @brief Default destructor. */ + virtual ~sparse_set() = default; + + /*! @brief Default move assignment operator. @return This sparse set. */ + sparse_set & operator=(sparse_set &&) = default; + + /** + * @brief Increases the capacity of a sparse set. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + void reserve(const size_type cap) { + packed.reserve(cap); + } + + /** + * @brief Returns the number of elements that a sparse set has currently + * allocated space for. + * @return Capacity of the sparse set. + */ + size_type capacity() const ENTT_NOEXCEPT { + return packed.capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + // conservative approach + if(packed.empty()) { + sparse.clear(); + } + + sparse.shrink_to_fit(); + packed.shrink_to_fit(); + } + + /** + * @brief Returns the extent of a sparse set. + * + * The extent of a sparse set is also the size of the internal sparse array. + * There is no guarantee that the internal packed array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Extent of the sparse set. + */ + size_type extent() const ENTT_NOEXCEPT { + return sparse.size() * entt_per_page; + } + + /** + * @brief Returns the number of elements in a sparse set. + * + * The number of elements is also the size of the internal packed array. + * There is no guarantee that the internal sparse array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Number of elements. + */ + size_type size() const ENTT_NOEXCEPT { + return packed.size(); + } + + /** + * @brief Checks whether a sparse set is empty. + * @return True if the sparse set is empty, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return packed.empty(); + } + + /** + * @brief Direct access to the internal packed array. + * + * The returned pointer is such that range `[data(), data() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order, even though `respect` has been + * previously invoked. Internal data structures arrange elements to maximize + * performance. Accessing them directly gives a performance boost but less + * guarantees. Use `begin` and `end` if you want to iterate the sparse set + * in the expected order. + * + * @return A pointer to the internal packed array. + */ + const entity_type * data() const ENTT_NOEXCEPT { + return packed.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * The returned iterator points to the first entity of the internal packed + * array. If the sparse set is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Random access iterators stay true to the order imposed by a call to + * `respect`. + * + * @return An iterator to the first entity of the internal packed array. + */ + iterator begin() const ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = packed.size(); + return iterator{packed, pos}; + } + + /** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last entity in + * the internal packed array. Attempting to dereference the returned + * iterator results in undefined behavior. + * + * @note + * Random access iterators stay true to the order imposed by a call to + * `respect`. + * + * @return An iterator to the element following the last entity of the + * internal packed array. + */ + iterator end() const ENTT_NOEXCEPT { + return iterator{packed, {}}; + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + iterator find(const entity_type entt) const { + return contains(entt) ? --(end() - index(entt)) : end(); + } + + /** + * @brief Checks if a sparse set contains an entity. + * @param entt A valid entity identifier. + * @return True if the sparse set contains the entity, false otherwise. + */ + bool contains(const entity_type entt) const { + const auto curr = page(entt); + // testing against null permits to avoid accessing the packed array + return (curr < sparse.size() && sparse[curr] && sparse[curr][offset(entt)] != null); + } + + /*! @copydoc contains */ + [[deprecated("use ::contains instead")]] + bool has(const entity_type entt) const { + return contains(entt); + } + + /** + * @brief Returns the position of an entity in a sparse set. + * + * @warning + * Attempting to get the position of an entity that doesn't belong to the + * sparse set results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set doesn't contain the given entity. + * + * @param entt A valid entity identifier. + * @return The position of the entity in the sparse set. + */ + size_type index(const entity_type entt) const { + ENTT_ASSERT(contains(entt)); + return size_type(sparse[page(entt)][offset(entt)]); + } + + /** + * @brief Assigns an entity to a sparse set. + * + * @warning + * Attempting to assign an entity that already belongs to the sparse set + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set already contains the given entity. + * + * @param entt A valid entity identifier. + */ + void emplace(const entity_type entt) { + ENTT_ASSERT(!contains(entt)); + assure(page(entt))[offset(entt)] = entity_type(packed.size()); + packed.push_back(entt); + } + + /*! @copydoc emplace */ + [[deprecated("use ::emplace instead")]] + void construct(const entity_type entt) { + emplace(entt); + } + + /** + * @brief Assigns one or more entities to a sparse set. + * + * @warning + * Attempting to assign an entity that already belongs to the sparse set + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set already contains the given entity. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void insert(It first, It last) { + auto next = packed.size(); + packed.insert(packed.end(), first, last); + + while(first != last) { + const auto entt = *(first++); + ENTT_ASSERT(!contains(entt)); + assure(page(entt))[offset(entt)] = entity_type(next++); + } + } + + /*! @copydoc insert */ + template + [[deprecated("use ::insert instead")]] + void construct(It first, It last) { + insert(std::move(first), std::move(last)); + } + + /** + * @brief Removes an entity from a sparse set. + * + * @warning + * Attempting to remove an entity that doesn't belong to the sparse set + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set doesn't contain the given entity. + * + * @param entt A valid entity identifier. + */ + void erase(const entity_type entt) { + ENTT_ASSERT(contains(entt)); + const auto curr = page(entt); + const auto pos = offset(entt); + packed[size_type(sparse[curr][pos])] = entity_type(packed.back()); + sparse[page(packed.back())][offset(packed.back())] = sparse[curr][pos]; + sparse[curr][pos] = null; + packed.pop_back(); + } + + /*! @copydoc erase */ + [[deprecated("use ::erase instead")]] + void destroy(const entity_type entt) { + erase(entt); + } + + /** + * @brief Swaps two entities in the internal packed array. + * + * For what it's worth, this function affects both the internal sparse array + * and the internal packed array. Users should not care of that anyway. + * + * @warning + * Attempting to swap entities that don't belong to the sparse set results + * in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set doesn't contain the given entities. + * + * @param lhs A valid entity identifier. + * @param rhs A valid entity identifier. + */ + virtual void swap(const entity_type lhs, const entity_type rhs) { + auto &from = sparse[page(lhs)][offset(lhs)]; + auto &to = sparse[page(rhs)][offset(rhs)]; + std::swap(packed[size_type(from)], packed[size_type(to)]); + std::swap(from, to); + } + + /** + * @brief Sort elements according to the given comparison function. + * + * Sort the elements so that iterating the range with a couple of iterators + * returns them in the expected order. See `begin` and `end` for more + * details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * @endcode + * + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @note + * Attempting to iterate elements using a raw pointer returned by a call to + * `data` gives no guarantees on the order, even though `sort` has been + * invoked. + * + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(iterator first, iterator last, Compare compare, Sort algo = Sort{}, Args &&... args) { + ENTT_ASSERT(!(last < first)); + ENTT_ASSERT(!(last > end())); + + const auto length = std::distance(first, last); + const auto skip = std::distance(last, end()); + const auto to = packed.rend() - skip; + const auto from = to - length; + + algo(from, to, std::move(compare), std::forward(args)...); + + for(size_type pos = skip, end = skip+length; pos < end; ++pos) { + sparse[page(packed[pos])][offset(packed[pos])] = entity_type(pos); + } + } + + /** + * @brief Sort elements according to the given comparison function. + * + * @sa sort + * + * This function is a slightly slower version of `sort` that invokes the + * caller to indicate which entities are swapped.
+ * It's recommended when the caller wants to sort its own data structures to + * align them with the order induced in the sparse set. + * + * The signature of the callback should be equivalent to the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * @endcode + * + * @tparam Apply Type of function object to invoke to notify the caller. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param apply A valid function object to use as a callback. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void arrange(iterator first, iterator last, Apply apply, Compare compare, Sort algo = Sort{}, Args &&... args) { + ENTT_ASSERT(!(last < first)); + ENTT_ASSERT(!(last > end())); + + const auto length = std::distance(first, last); + const auto skip = std::distance(last, end()); + const auto to = packed.rend() - skip; + const auto from = to - length; + + algo(from, to, std::move(compare), std::forward(args)...); + + for(size_type pos = skip, end = skip+length; pos < end; ++pos) { + auto curr = pos; + auto next = index(packed[curr]); + + while(curr != next) { + apply(packed[curr], packed[next]); + sparse[page(packed[curr])][offset(packed[curr])] = entity_type(curr); + + curr = next; + next = index(packed[curr]); + } + } + } + + /** + * @brief Sort entities according to their order in another sparse set. + * + * Entities that are part of both the sparse sets are ordered internally + * according to the order they have in `other`. All the other entities goes + * to the end of the list and there are no guarantees on their order.
+ * In other terms, this function can be used to impose the same order on two + * sets by using one of them as a master and the other one as a slave. + * + * Iterating the sparse set with a couple of iterators returns elements in + * the expected order after a call to `respect`. See `begin` and `end` for + * more details. + * + * @note + * Attempting to iterate elements using a raw pointer returned by a call to + * `data` gives no guarantees on the order, even though `respect` has been + * invoked. + * + * @param other The sparse sets that imposes the order of the entities. + */ + void respect(const sparse_set &other) { + const auto to = other.end(); + auto from = other.begin(); + + size_type pos = packed.size() - 1; + + while(pos && from != to) { + if(contains(*from)) { + if(*from != packed[pos]) { + swap(packed[pos], *from); + } + + --pos; + } + + ++from; + } + } + + /** + * @brief Clears a sparse set. + */ + void clear() ENTT_NOEXCEPT { + sparse.clear(); + packed.clear(); + } + +private: + std::vector sparse; + std::vector packed; +}; + + +} + + +#endif + +// #include "storage.hpp" +#ifndef ENTT_ENTITY_STORAGE_HPP +#define ENTT_ENTITY_STORAGE_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" + +// #include "sparse_set.hpp" + +// #include "entity.hpp" + + + +namespace entt { + + +/** + * @brief Basic storage implementation. + * + * This class is a refinement of a sparse set that associates an object to an + * entity. The main purpose of this class is to extend sparse sets to store + * components in a registry. It guarantees fast access both to the elements and + * to the entities. + * + * @note + * Entities and objects have the same order. It's guaranteed both in case of raw + * access (either to entities or objects) and when using random or input access + * iterators. + * + * @note + * Internal data structures arrange elements to maximize performance. Because of + * that, there are no guarantees that elements have the expected order when + * iterate directly the internal packed array (see `raw` and `size` member + * functions for that). Use `begin` and `end` instead. + * + * @warning + * Empty types aren't explicitly instantiated. Therefore, many of the functions + * normally available for non-empty types will not be available for empty ones. + * + * @sa sparse_set + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects assigned to the entities. + */ +template> +class storage: public sparse_set { + static_assert(std::is_move_constructible_v); + static_assert(std::is_move_assignable_v); + + using underlying_type = sparse_set; + using traits_type = entt_traits>; + + template + class storage_iterator final { + friend class storage; + + using instance_type = std::conditional_t, std::vector>; + using index_type = typename traits_type::difference_type; + + storage_iterator(instance_type &ref, const index_type idx) ENTT_NOEXCEPT + : instances{&ref}, index{idx} + {} + + public: + using difference_type = index_type; + using value_type = Type; + using pointer = std::conditional_t; + using reference = std::conditional_t; + using iterator_category = std::random_access_iterator_tag; + + storage_iterator() ENTT_NOEXCEPT = default; + + storage_iterator & operator++() ENTT_NOEXCEPT { + return --index, *this; + } + + storage_iterator operator++(int) ENTT_NOEXCEPT { + storage_iterator orig = *this; + return operator++(), orig; + } + + storage_iterator & operator--() ENTT_NOEXCEPT { + return ++index, *this; + } + + storage_iterator operator--(int) ENTT_NOEXCEPT { + storage_iterator orig = *this; + return operator--(), orig; + } + + storage_iterator & operator+=(const difference_type value) ENTT_NOEXCEPT { + index -= value; + return *this; + } + + storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + storage_iterator copy = *this; + return (copy += value); + } + + storage_iterator & operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + difference_type operator-(const storage_iterator &other) const ENTT_NOEXCEPT { + return other.index - index; + } + + reference operator[](const difference_type value) const ENTT_NOEXCEPT { + const auto pos = size_type(index-value-1); + return (*instances)[pos]; + } + + bool operator==(const storage_iterator &other) const ENTT_NOEXCEPT { + return other.index == index; + } + + bool operator!=(const storage_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + bool operator<(const storage_iterator &other) const ENTT_NOEXCEPT { + return index > other.index; + } + + bool operator>(const storage_iterator &other) const ENTT_NOEXCEPT { + return index < other.index; + } + + bool operator<=(const storage_iterator &other) const ENTT_NOEXCEPT { + return !(*this > other); + } + + bool operator>=(const storage_iterator &other) const ENTT_NOEXCEPT { + return !(*this < other); + } + + pointer operator->() const ENTT_NOEXCEPT { + const auto pos = size_type(index-1); + return &(*instances)[pos]; + } + + reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + private: + instance_type *instances; + index_type index; + }; + +public: + /*! @brief Type of the objects associated with the entities. */ + using object_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = storage_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = storage_iterator; + + /** + * @brief Increases the capacity of a storage. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + void reserve(const size_type cap) { + underlying_type::reserve(cap); + instances.reserve(cap); + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + underlying_type::shrink_to_fit(); + instances.shrink_to_fit(); + } + + /** + * @brief Direct access to the array of objects. + * + * The returned pointer is such that range `[raw(), raw() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order, even though either `sort` or + * `respect` has been previously invoked. Internal data structures arrange + * elements to maximize performance. Accessing them directly gives a + * performance boost but less guarantees. Use `begin` and `end` if you want + * to iterate the storage in the expected order. + * + * @return A pointer to the array of objects. + */ + const object_type * raw() const ENTT_NOEXCEPT { + return instances.data(); + } + + /*! @copydoc raw */ + object_type * raw() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).raw()); + } + + /** + * @brief Returns an iterator to the beginning. + * + * The returned iterator points to the first instance of the given type. If + * the storage is empty, the returned iterator will be equal to `end()`. + * + * @note + * Random access iterators stay true to the order imposed by a call to + * either `sort` or `respect`. + * + * @return An iterator to the first instance of the given type. + */ + const_iterator cbegin() const ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = underlying_type::size(); + return const_iterator{instances, pos}; + } + + /*! @copydoc cbegin */ + const_iterator begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /*! @copydoc begin */ + iterator begin() ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = underlying_type::size(); + return iterator{instances, pos}; + } + + /** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the given type. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Random access iterators stay true to the order imposed by a call to + * either `sort` or `respect`. + * + * @return An iterator to the element following the last instance of the + * given type. + */ + const_iterator cend() const ENTT_NOEXCEPT { + return const_iterator{instances, {}}; + } + + /*! @copydoc cend */ + const_iterator end() const ENTT_NOEXCEPT { + return cend(); + } + + /*! @copydoc end */ + iterator end() ENTT_NOEXCEPT { + return iterator{instances, {}}; + } + + /** + * @brief Returns the object associated with an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage doesn't contain the given entity. + * + * @param entt A valid entity identifier. + * @return The object associated with the entity. + */ + const object_type & get(const entity_type entt) const { + return instances[underlying_type::index(entt)]; + } + + /*! @copydoc get */ + object_type & get(const entity_type entt) { + return const_cast(std::as_const(*this).get(entt)); + } + + /** + * @brief Returns a pointer to the object associated with an entity, if any. + * @param entt A valid entity identifier. + * @return The object associated with the entity, if any. + */ + const object_type * try_get(const entity_type entt) const { + return underlying_type::contains(entt) ? (instances.data() + underlying_type::index(entt)) : nullptr; + } + + /*! @copydoc try_get */ + object_type * try_get(const entity_type entt) { + return const_cast(std::as_const(*this).try_get(entt)); + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * This version accept both types that can be constructed in place directly + * and types like aggregates that do not work well with a placement new as + * performed usually under the hood during an _emplace back_. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage already contains the given entity. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid entity identifier. + * @param args Parameters to use to construct an object for the entity. + */ + template + void emplace(const entity_type entt, Args &&... args) { + if constexpr(std::is_aggregate_v) { + instances.push_back(Type{std::forward(args)...}); + } else { + instances.emplace_back(std::forward(args)...); + } + + // entity goes after component in case constructor throws + underlying_type::emplace(entt); + } + + /*! @copydoc emplace */ + template + [[deprecated("use ::emplace instead")]] + void construct(const entity_type entt, Args &&... args) { + emplace(entt, std::forward(args)...); + } + + /** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given instance. + * + * @warning + * Attempting to assign an entity that already belongs to the storage + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage already contains the given entity. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the object to construct. + */ + template + void insert(It first, It last, const object_type &value = {}) { + instances.insert(instances.end(), std::distance(first, last), value); + // entities go after components in case constructors throw + underlying_type::insert(first, last); + } + + /*! @copydoc insert */ + template + [[deprecated("use ::insert instead")]] + std::enable_if_t::value_type, entity_type>, void> + construct(It first, It last, const object_type &value = {}) { + insert(std::move(first), std::move(last), value); + } + + /** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given range. + * + * @sa construct + * + * @tparam EIt Type of input iterator. + * @tparam CIt Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param from An iterator to the first element of the range of objects. + * @param to An iterator past the last element of the range of objects. + */ + template + void insert(EIt first, EIt last, CIt from, CIt to) { + instances.insert(instances.end(), from, to); + // entities go after components in case constructors throw + underlying_type::insert(first, last); + } + + /*! @copydoc insert */ + template + [[deprecated("use ::insert instead")]] + std::enable_if_t::value_type, entity_type>, void> + construct(EIt first, EIt last, CIt value) { + insert(std::move(first), std::move(last), std::move(value)); + } + + /** + * @brief Removes an entity from a storage and destroys its object. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage doesn't contain the given entity. + * + * @param entt A valid entity identifier. + */ + void erase(const entity_type entt) { + auto other = std::move(instances.back()); + instances[underlying_type::index(entt)] = std::move(other); + instances.pop_back(); + underlying_type::erase(entt); + } + + /*! @copydoc erase */ + [[deprecated("use ::erase instead")]] + void destroy(const entity_type entt) { + erase(entt); + } + + /** + * @brief Swaps entities and objects in the internal packed arrays. + * + * @warning + * Attempting to swap entities that don't belong to the sparse set results + * in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * sparse set doesn't contain the given entities. + * + * @param lhs A valid entity identifier. + * @param rhs A valid entity identifier. + */ + void swap(const entity_type lhs, const entity_type rhs) override { + std::swap(instances[underlying_type::index(lhs)], instances[underlying_type::index(rhs)]); + underlying_type::swap(lhs, rhs); + } + + /** + * @brief Sort elements according to the given comparison function. + * + * Sort the elements so that iterating the range with a couple of iterators + * returns them in the expected order. See `begin` and `end` for more + * details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * bool(const Type &, const Type &); + * @endcode + * + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @note + * Attempting to iterate elements using a raw pointer returned by a call to + * either `data` or `raw` gives no guarantees on the order, even though + * `sort` has been invoked. + * + * @warning + * Empty types are never instantiated. Therefore, only comparison function + * objects that require to return entities rather than components are + * accepted. + * + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(iterator first, iterator last, Compare compare, Sort algo = Sort{}, Args &&... args) { + ENTT_ASSERT(!(last < first)); + ENTT_ASSERT(!(last > end())); + + const auto from = underlying_type::begin() + std::distance(begin(), first); + const auto to = from + std::distance(first, last); + + const auto apply = [this](const auto lhs, const auto rhs) { + std::swap(instances[underlying_type::index(lhs)], instances[underlying_type::index(rhs)]); + }; + + if constexpr(std::is_invocable_v) { + underlying_type::arrange(from, to, std::move(apply), [this, compare = std::move(compare)](const auto lhs, const auto rhs) { + return compare(std::as_const(instances[underlying_type::index(lhs)]), std::as_const(instances[underlying_type::index(rhs)])); + }, std::move(algo), std::forward(args)...); + } else { + underlying_type::arrange(from, to, std::move(apply), std::move(compare), std::move(algo), std::forward(args)...); + } + } + + /*! @brief Clears a storage. */ + void clear() { + underlying_type::clear(); + instances.clear(); + } + +private: + std::vector instances; +}; + + +/*! @copydoc storage */ +template +class storage>: public sparse_set { + using traits_type = entt_traits>; + using underlying_type = sparse_set; + +public: + /*! @brief Type of the objects associated with the entities. */ + using object_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage already contains the given entity. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid entity identifier. + * @param args Parameters to use to construct an object for the entity. + */ + template + void emplace(const entity_type entt, Args &&... args) { + [[maybe_unused]] object_type instance{std::forward(args)...}; + underlying_type::emplace(entt); + } + + /*! @copydoc emplace */ + template + [[deprecated("use ::emplace instead")]] + void construct(const entity_type entt, Args &&... args) { + emplace(entt, std::forward(args)...); + } + + /** + * @brief Assigns one or more entities to a storage. + * + * @warning + * Attempting to assign an entity that already belongs to the storage + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage already contains the given entity. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void insert(It first, It last, const object_type & = {}) { + underlying_type::insert(first, last); + } + + /** + * @copydoc insert + * @param value An instance of the object to construct. + */ + template + [[deprecated("use ::insert instead")]] + std::enable_if_t::value_type, entity_type>, void> + construct(It first, It last, const object_type &value = {}) { + insert(std::move(first), std::move(last), value); + } +}; + + +} + + +#endif + +// #include "utility.hpp" +#ifndef ENTT_ENTITY_UTILITY_HPP +#define ENTT_ENTITY_UTILITY_HPP + + +// #include "../core/type_traits.hpp" + + + +namespace entt { + + +/** + * @brief Alias for exclusion lists. + * @tparam Type List of types. + */ +template +struct exclude_t: type_list {}; + + +/** + * @brief Variable template for exclusion lists. + * @tparam Type List of types. + */ +template +inline constexpr exclude_t exclude{}; + + +/** + * @brief Alias for lists of observed components. + * @tparam Type List of types. + */ +template +struct get_t: type_list{}; + + +/** + * @brief Variable template for lists of observed components. + * @tparam Type List of types. + */ +template +inline constexpr get_t get{}; + + +} + + +#endif + +// #include "entity.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Group. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +class basic_group; + + +/** + * @brief Non-owning group. + * + * A non-owning group returns all entities and only the entities that have at + * least the given components. Moreover, it's guaranteed that the entity list + * is tightly packed in memory for fast iterations. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Groups share references to the underlying data structures of the registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by all the + * groups.
+ * Moreover, sorting a non-owning group affects all the instances of the same + * group (it means that users don't have to call `sort` on each instance to sort + * all of them because they _share_ entities and components). + * + * @warning + * Lifetime of a group must not overcome that of the registry that generated it. + * In any other case, attempting to use a group results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Exclude Types of components used to filter the group. + * @tparam Get Type of components observed by the group. + */ +template +class basic_group, get_t> { + /*! @brief A registry is allowed to create groups. */ + friend class basic_registry; + + template + using pool_type = std::conditional_t, const storage>, storage>; + + // we could use pool_type &..., but vs complains about it and refuses to compile for unknown reasons (most likely a bug) + basic_group(sparse_set &ref, storage> &... gpool) ENTT_NOEXCEPT + : handler{&ref}, + pools{&gpool...} + {} + + template + void traverse(Func func, type_list) const { + for(const auto entt: *handler) { + if constexpr(std::is_invocable_v({}))...>) { + func(std::get *>(pools)->get(entt)...); + } else { + func(entt, std::get *>(pools)->get(entt)...); + } + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Input iterator type. */ + using iterator = typename sparse_set::iterator; + + /** + * @brief Returns the number of existing components of the given type. + * @tparam Component Type of component of which to return the size. + * @return Number of existing components of the given type. + */ + template + size_type size() const ENTT_NOEXCEPT { + return std::get *>(pools)->size(); + } + + /** + * @brief Returns the number of entities that have the given components. + * @return Number of entities that have the given components. + */ + size_type size() const ENTT_NOEXCEPT { + return handler->size(); + } + + /** + * @brief Returns the number of elements that a group has currently + * allocated space for. + * @return Capacity of the group. + */ + size_type capacity() const ENTT_NOEXCEPT { + return handler->capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + handler->shrink_to_fit(); + } + + /** + * @brief Checks whether a group or some pools are empty. + * @tparam Component Types of components in which one is interested. + * @return True if the group or the pools are empty, false otherwise. + */ + template + bool empty() const ENTT_NOEXCEPT { + if constexpr(sizeof...(Component) == 0) { + return handler->empty(); + } else { + return (std::get *>(pools)->empty() && ...); + } + } + + /** + * @brief Direct access to the list of components of a given pool. + * + * The returned pointer is such that range + * `[raw(), raw() + size()]` is always a + * valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the components. Use `begin` and + * `end` if you want to iterate the group in the expected order. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of components. + */ + template + Component * raw() const ENTT_NOEXCEPT { + return std::get *>(pools)->raw(); + } + + /** + * @brief Direct access to the list of entities of a given pool. + * + * The returned pointer is such that range + * `[data(), data() + size()]` is always a + * valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the entities. Use `begin` and + * `end` if you want to iterate the group in the expected order. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of entities. + */ + template + const entity_type * data() const ENTT_NOEXCEPT { + return std::get *>(pools)->data(); + } + + /** + * @brief Direct access to the list of entities. + * + * The returned pointer is such that range `[data(), data() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the entities. Use `begin` and + * `end` if you want to iterate the group in the expected order. + * + * @return A pointer to the array of entities. + */ + const entity_type * data() const ENTT_NOEXCEPT { + return handler->data(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the group is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given components. + */ + iterator begin() const ENTT_NOEXCEPT { + return handler->begin(); + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ + iterator end() const ENTT_NOEXCEPT { + return handler->end(); + } + + /** + * @brief Returns the first entity that has the given components, if any. + * @return The first entity that has the given components if one exists, the + * null entity otherwise. + */ + entity_type front() const { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity that has the given components, if any. + * @return The last entity that has the given components if one exists, the + * null entity otherwise. + */ + entity_type back() const { + const auto it = std::make_reverse_iterator(end()); + return it != std::make_reverse_iterator(begin()) ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + iterator find(const entity_type entt) const { + const auto it = handler->find(entt); + return it != end() && *it == entt ? it : end(); + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid entity identifier. + * @return True if the group contains the given entity, false otherwise. + */ + bool contains(const entity_type entt) const { + return handler->contains(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `registry::get` during iterations. It has + * far better performance than its counterpart. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the group + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * group doesn't contain the given entity. + * + * @tparam Component Types of components to get. + * @param entt A valid entity identifier. + * @return The components assigned to the entity. + */ + template + decltype(auto) get([[maybe_unused]] const entity_type entt) const { + ENTT_ASSERT(contains(entt)); + + if constexpr(sizeof...(Component) == 1) { + return (std::get *>(pools)->get(entt), ...); + } else { + return std::tuple({}))...>{get(entt)...}; + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + using get_type_list = type_list_cat_t, type_list>...>; + traverse(std::move(func), get_type_list{}); + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + [[deprecated("use ::each instead")]] + void less(Func func) const { + each(std::move(func)); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * Sort the group so that iterating it with a couple of iterators returns + * entities and components in the expected order. See `begin` and `end` for + * more details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Component &..., const Component &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Component` are such that they are iterated by the group.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @note + * Attempting to iterate elements using a raw pointer returned by a call to + * either `data` or `raw` gives no guarantees on the order, even though + * `sort` has been invoked. + * + * @tparam Component Optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { + if constexpr(sizeof...(Component) == 0) { + static_assert(std::is_invocable_v); + handler->sort(handler->begin(), handler->end(), std::move(compare), std::move(algo), std::forward(args)...); + } else if constexpr(sizeof...(Component) == 1) { + handler->sort(handler->begin(), handler->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare((std::get *>(pools)->get(lhs), ...), (std::get *>(pools)->get(rhs), ...)); + }, std::move(algo), std::forward(args)...); + } else { + handler->sort(handler->begin(), handler->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare(std::tuple({}))...>{std::get *>(pools)->get(lhs)...}, std::tuple({}))...>{std::get *>(pools)->get(rhs)...}); + }, std::move(algo), std::forward(args)...); + } + } + + /** + * @brief Sort the shared pool of entities according to the given component. + * + * Non-owning groups of the same type share with the registry a pool of + * entities with its own order that doesn't depend on the order of any pool + * of components. Users can order the underlying data structure so that it + * respects the order of the pool of the given component. + * + * @note + * The shared pool of entities and thus its order is affected by the changes + * to each and every pool that it tracks. Therefore changes to those pools + * can quickly ruin the order imposed to the pool of entities shared between + * the non-owning groups. + * + * @tparam Component Type of component to use to impose the order. + */ + template + void sort() const { + handler->respect(*std::get *>(pools)); + } + +private: + sparse_set *handler; + const std::tuple *...> pools; +}; + + +/** + * @brief Owning group. + * + * Owning groups return all entities and only the entities that have at least + * the given components. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that the lists of owned components are tightly packed in + * memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned components and all instances have + * the same order in memory. + * + * The more types of components are owned by a group, the faster it is to + * iterate them. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Groups share references to the underlying data structures of the registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by all the + * groups. + * Moreover, sorting an owning group affects all the instance of the same group + * (it means that users don't have to call `sort` on each instance to sort all + * of them because they share the underlying data structure). + * + * @warning + * Lifetime of a group must not overcome that of the registry that generated it. + * In any other case, attempting to use a group results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Exclude Types of components used to filter the group. + * @tparam Get Types of components observed by the group. + * @tparam Owned Types of components owned by the group. + */ +template +class basic_group, get_t, Owned...> { + /*! @brief A registry is allowed to create groups. */ + friend class basic_registry; + + template + using pool_type = std::conditional_t, const storage>, storage>; + + template + using component_iterator = decltype(std::declval>().begin()); + + // we could use pool_type &..., but vs complains about it and refuses to compile for unknown reasons (most likely a bug) + basic_group(const std::size_t &ref, const std::size_t &extent, storage> &... opool, storage> &... gpool) ENTT_NOEXCEPT + : pools{&opool..., &gpool...}, + length{&extent}, + super{&ref} + {} + + template + void traverse(Func func, type_list, type_list) const { + [[maybe_unused]] auto it = std::make_tuple((std::get *>(pools)->end() - *length)...); + [[maybe_unused]] auto data = std::get<0>(pools)->sparse_set::end() - *length; + + for(auto next = *length; next; --next) { + if constexpr(std::is_invocable_v({}))..., decltype(get({}))...>) { + if constexpr(sizeof...(Weak) == 0) { + func(*(std::get>(it)++)...); + } else { + const auto entt = *(data++); + func(*(std::get>(it)++)..., std::get *>(pools)->get(entt)...); + } + } else { + const auto entt = *(data++); + func(entt, *(std::get>(it)++)..., std::get *>(pools)->get(entt)...); + } + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Input iterator type. */ + using iterator = typename sparse_set::iterator; + + /** + * @brief Returns the number of existing components of the given type. + * @tparam Component Type of component of which to return the size. + * @return Number of existing components of the given type. + */ + template + size_type size() const ENTT_NOEXCEPT { + return std::get *>(pools)->size(); + } + + /** + * @brief Returns the number of entities that have the given components. + * @return Number of entities that have the given components. + */ + size_type size() const ENTT_NOEXCEPT { + return *length; + } + + /** + * @brief Checks whether a group or some pools are empty. + * @tparam Component Types of components in which one is interested. + * @return True if the group or the pools are empty, false otherwise. + */ + template + bool empty() const ENTT_NOEXCEPT { + if constexpr(sizeof...(Component) == 0) { + return !*length; + } else { + return (std::get *>(pools)->empty() && ...); + } + } + + /** + * @brief Direct access to the list of components of a given pool. + * + * The returned pointer is such that range + * `[raw(), raw() + size()]` is always a + * valid range, even if the container is empty.
+ * Moreover, in case the group owns the given component, the range + * `[raw(), raw() + size()]` is such that it contains + * the instances that are part of the group itself. + * + * @note + * There are no guarantees on the order of the components. Use `begin` and + * `end` if you want to iterate the group in the expected order. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of components. + */ + template + Component * raw() const ENTT_NOEXCEPT { + return std::get *>(pools)->raw(); + } + + /** + * @brief Direct access to the list of entities of a given pool. + * + * The returned pointer is such that range + * `[data(), data() + size()]` is always a + * valid range, even if the container is empty.
+ * Moreover, in case the group owns the given component, the range + * `[data(), data() + size()]` is such that it + * contains the entities that are part of the group itself. + * + * @note + * There are no guarantees on the order of the entities. Use `begin` and + * `end` if you want to iterate the group in the expected order. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of entities. + */ + template + const entity_type * data() const ENTT_NOEXCEPT { + return std::get *>(pools)->data(); + } + + /** + * @brief Direct access to the list of entities. + * + * The returned pointer is such that range `[data(), data() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the entities. Use `begin` and + * `end` if you want to iterate the group in the expected order. + * + * @return A pointer to the array of entities. + */ + const entity_type * data() const ENTT_NOEXCEPT { + return std::get<0>(pools)->data(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the group is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given components. + */ + iterator begin() const ENTT_NOEXCEPT { + return std::get<0>(pools)->sparse_set::end() - *length; + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ + iterator end() const ENTT_NOEXCEPT { + return std::get<0>(pools)->sparse_set::end(); + } + + /** + * @brief Returns the first entity that has the given components, if any. + * @return The first entity that has the given components if one exists, the + * null entity otherwise. + */ + entity_type front() const { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity that has the given components, if any. + * @return The last entity that has the given components if one exists, the + * null entity otherwise. + */ + entity_type back() const { + const auto it = std::make_reverse_iterator(end()); + return it != std::make_reverse_iterator(begin()) ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + iterator find(const entity_type entt) const { + const auto it = std::get<0>(pools)->find(entt); + return it != end() && it >= begin() && *it == entt ? it : end(); + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid entity identifier. + * @return True if the group contains the given entity, false otherwise. + */ + bool contains(const entity_type entt) const { + return std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); + } + + /** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `registry::get` during iterations. It has + * far better performance than its counterpart. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the group + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * group doesn't contain the given entity. + * + * @tparam Component Types of components to get. + * @param entt A valid entity identifier. + * @return The components assigned to the entity. + */ + template + decltype(auto) get([[maybe_unused]] const entity_type entt) const { + ENTT_ASSERT(contains(entt)); + + if constexpr(sizeof...(Component) == 1) { + return (std::get *>(pools)->get(entt), ...); + } else { + return std::tuple({}))...>{get(entt)...}; + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + using owned_type_list = type_list_cat_t, type_list>...>; + using get_type_list = type_list_cat_t, type_list>...>; + traverse(std::move(func), owned_type_list{}, get_type_list{}); + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + [[deprecated("use ::each instead")]] + void less(Func func) const { + each(std::move(func)); + } + + /** + * @brief Checks whether the group can be sorted. + * @return True if the group can be sorted, false otherwise. + */ + bool sortable() const ENTT_NOEXCEPT { + constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); + return *super == size; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * Sort the group so that iterating it with a couple of iterators returns + * entities and components in the expected order. See `begin` and `end` for + * more details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Component &, const Component &); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Component` are either owned types or not but still such that they + * are iterated by the group.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @note + * Attempting to iterate elements using a raw pointer returned by a call to + * either `data` or `raw` gives no guarantees on the order, even though + * `sort` has been invoked. + * + * @tparam Component Optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { + ENTT_ASSERT(sortable()); + auto *cpool = std::get<0>(pools); + + if constexpr(sizeof...(Component) == 0) { + static_assert(std::is_invocable_v); + cpool->sort(cpool->end()-*length, cpool->end(), std::move(compare), std::move(algo), std::forward(args)...); + } else if constexpr(sizeof...(Component) == 1) { + cpool->sort(cpool->end()-*length, cpool->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare((std::get *>(pools)->get(lhs), ...), (std::get *>(pools)->get(rhs), ...)); + }, std::move(algo), std::forward(args)...); + } else { + cpool->sort(cpool->end()-*length, cpool->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { + return compare(std::tuple({}))...>{std::get *>(pools)->get(lhs)...}, std::tuple({}))...>{std::get *>(pools)->get(rhs)...}); + }, std::move(algo), std::forward(args)...); + } + + [this](auto *head, auto *... other) { + for(auto next = *length; next; --next) { + const auto pos = next - 1; + [[maybe_unused]] const auto entt = head->data()[pos]; + (other->swap(other->data()[pos], entt), ...); + } + }(std::get *>(pools)...); + } + +private: + const std::tuple *..., pool_type *...> pools; + const size_type *length; + const size_type *super; +}; + + +} + + +#endif + +// #include "runtime_view.hpp" +#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP +#define ENTT_ENTITY_RUNTIME_VIEW_HPP + + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "sparse_set.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Runtime view. + * + * Runtime views iterate over those entities that have at least all the given + * components in their bags. During initialization, a runtime view looks at the + * number of entities available for each component and picks up a reference to + * the smallest set of candidate entities in order to get a performance boost + * when iterate.
+ * Order of elements during iterations are highly dependent on the order of the + * underlying data structures. See sparse_set and its specializations for more + * details. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all the other cases, modifying the pools of the given components in any + * way invalidates all the iterators and using them results in undefined + * behavior. + * + * @note + * Views share references to the underlying data structures of the registry that + * generated them. Therefore any change to the entities and to the components + * made by means of the registry are immediately reflected by the views, unless + * a pool was missing when the view was built (in this case, the view won't + * have a valid reference and won't be updated accordingly). + * + * @warning + * Lifetime of a view must not overcome that of the registry that generated it. + * In any other case, attempting to use a view results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_runtime_view { + /*! @brief A registry is allowed to create views. */ + friend class basic_registry; + + using underlying_iterator = typename sparse_set::iterator; + + class view_iterator final { + friend class basic_runtime_view; + + using direct_type = std::vector *>; + + view_iterator(const direct_type &all, underlying_iterator curr) ENTT_NOEXCEPT + : pools{&all}, + it{curr} + { + if(it != (*pools)[0]->end() && !valid()) { + ++(*this); + } + } + + bool valid() const { + return std::all_of(pools->begin()++, pools->end(), [entt = *it](const auto *curr) { + return curr->contains(entt); + }); + } + + public: + using difference_type = typename underlying_iterator::difference_type; + using value_type = typename underlying_iterator::value_type; + using pointer = typename underlying_iterator::pointer; + using reference = typename underlying_iterator::reference; + using iterator_category = std::bidirectional_iterator_tag; + + view_iterator() ENTT_NOEXCEPT = default; + + view_iterator & operator++() { + while(++it != (*pools)[0]->end() && !valid()); + return *this; + } + + view_iterator operator++(int) { + view_iterator orig = *this; + return operator++(), orig; + } + + view_iterator & operator--() ENTT_NOEXCEPT { + while(--it != (*pools)[0]->begin() && !valid()); + return *this; + } + + view_iterator operator--(int) ENTT_NOEXCEPT { + view_iterator orig = *this; + return operator--(), orig; + } + + bool operator==(const view_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + bool operator!=(const view_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + pointer operator->() const { + return it.operator->(); + } + + reference operator*() const { + return *operator->(); + } + + private: + const direct_type *pools; + underlying_iterator it; + }; + + basic_runtime_view(std::vector *> others) ENTT_NOEXCEPT + : pools{std::move(others)} + { + const auto it = std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) { + return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size()); + }); + + // brings the best candidate (if any) on front of the vector + std::rotate(pools.begin(), it, pools.end()); + } + + bool valid() const { + return !pools.empty() && pools.front(); + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Input iterator type. */ + using iterator = view_iterator; + + /** + * @brief Estimates the number of entities that have the given components. + * @return Estimated number of entities that have the given components. + */ + size_type size() const { + return valid() ? pools.front()->size() : size_type{}; + } + + /** + * @brief Checks if the view is definitely empty. + * @return True if the view is definitely empty, false otherwise. + */ + bool empty() const { + return !valid() || pools.front()->empty(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given components. + */ + iterator begin() const { + iterator it{}; + + if(valid()) { + it = { pools, pools[0]->begin() }; + } + + return it; + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ + iterator end() const { + iterator it{}; + + if(valid()) { + it = { pools, pools[0]->end() }; + } + + return it; + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid entity identifier. + * @return True if the view contains the given entity, false otherwise. + */ + bool contains(const entity_type entt) const { + return valid() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *view) { + return view->find(entt) != view->end(); + }); + } + + /** + * @brief Iterates entities and applies the given function object to them. + * + * The function object is invoked for each entity. It is provided only with + * the entity itself. To get the components, users can use the registry with + * which the view was built.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const entity_type); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + for(const auto entity: *this) { + func(entity); + } + } + +private: + std::vector *> pools; +}; + + +} + + +#endif + +// #include "snapshot.hpp" +#ifndef ENTT_ENTITY_SNAPSHOT_HPP +#define ENTT_ENTITY_SNAPSHOT_HPP + + +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Utility class to create snapshots from a registry. + * + * A _snapshot_ can be either a dump of the entire registry or a narrower + * selection of components of interest.
+ * This type can be used in both cases if provided with a correctly configured + * output archive. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_snapshot { + /*! @brief A registry is allowed to create snapshots. */ + friend class basic_registry; + + using traits_type = entt_traits>; + + template + void get(Archive &archive, std::size_t sz, It first, It last) const { + archive(typename traits_type::entity_type(sz)); + + while(first != last) { + const auto entt = *(first++); + + if(reg->template has(entt)) { + if constexpr(std::is_empty_v) { + archive(entt); + } else { + archive(entt, reg->template get(entt)); + } + } + } + } + + template + void component(Archive &archive, It first, It last, std::index_sequence) const { + std::array size{}; + auto begin = first; + + while(begin != last) { + const auto entt = *(begin++); + ((reg->template has(entt) ? ++size[Indexes] : size[Indexes]), ...); + } + + (get(archive, size[Indexes], first, last), ...); + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot(const basic_registry &source) ENTT_NOEXCEPT + : reg{&source} + {} + + /*! @brief Default move constructor. */ + basic_snapshot(basic_snapshot &&) = default; + + /*! @brief Default move assignment operator. @return This snapshot. */ + basic_snapshot & operator=(basic_snapshot &&) = default; + + /** + * @brief Puts aside all the entities from the underlying registry. + * + * Entities are serialized along with their versions. Destroyed entities are + * taken in consideration as well by this function. + * + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @return An object of this type to continue creating the snapshot. + */ + template + const basic_snapshot & entities(Archive &archive) const { + const auto sz = reg->size(); + auto first = reg->data(); + const auto last = first + sz; + + archive(typename traits_type::entity_type(sz)); + + while(first != last) { + archive(*(first++)); + } + + return *this; + } + + /** + * @brief Deprecated function. Currently, it does nothing. + * @tparam Archive Type of output archive. + * @return An object of this type to continue creating the snapshot. + */ + template + [[deprecated("use ::entities instead, it exports now also destroyed entities")]] + const basic_snapshot & destroyed(Archive &) const { return *this; } + + /** + * @brief Puts aside the given components. + * + * Each instance is serialized together with the entity to which it belongs. + * Entities are serialized along with their versions. + * + * @tparam Component Types of components to serialize. + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @return An object of this type to continue creating the snapshot. + */ + template + const basic_snapshot & component(Archive &archive) const { + (component(archive, reg->template data(), reg->template data() + reg->template size()), ...); + return *this; + } + + /** + * @brief Puts aside the given components for the entities in a range. + * + * Each instance is serialized together with the entity to which it belongs. + * Entities are serialized along with their versions. + * + * @tparam Component Types of components to serialize. + * @tparam Archive Type of output archive. + * @tparam It Type of input iterator. + * @param archive A valid reference to an output archive. + * @param first An iterator to the first element of the range to serialize. + * @param last An iterator past the last element of the range to serialize. + * @return An object of this type to continue creating the snapshot. + */ + template + const basic_snapshot & component(Archive &archive, It first, It last) const { + component(archive, first, last, std::index_sequence_for{}); + return *this; + } + +private: + const basic_registry *reg; +}; + + +/** + * @brief Utility class to restore a snapshot as a whole. + * + * A snapshot loader requires that the destination registry be empty and loads + * all the data at once while keeping intact the identifiers that the entities + * originally had.
+ * An example of use is the implementation of a save/restore utility. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_snapshot_loader { + /*! @brief A registry is allowed to create snapshot loaders. */ + friend class basic_registry; + + using traits_type = entt_traits>; + + template + void assign(Archive &archive, Args... args) const { + typename traits_type::entity_type length{}; + archive(length); + + while(length--) { + entity_type entt{}; + + if constexpr(std::is_empty_v) { + archive(entt); + const auto entity = reg->valid(entt) ? entt : reg->create(entt); + ENTT_ASSERT(entity == entt); + reg->template emplace(args..., entt); + } else { + Type instance{}; + archive(entt, instance); + const auto entity = reg->valid(entt) ? entt : reg->create(entt); + ENTT_ASSERT(entity == entt); + reg->template emplace(args..., entt, std::as_const(instance)); + } + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot_loader(basic_registry &source) ENTT_NOEXCEPT + : reg{&source} + { + // restoring a snapshot as a whole requires a clean registry + ENTT_ASSERT(reg->empty()); + } + + /*! @brief Default move constructor. */ + basic_snapshot_loader(basic_snapshot_loader &&) = default; + + /*! @brief Default move assignment operator. @return This loader. */ + basic_snapshot_loader & operator=(basic_snapshot_loader &&) = default; + + /** + * @brief Restores entities that were in use during serialization. + * + * This function restores the entities that were in use during serialization + * and gives them the versions they originally had. + * + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A valid loader to continue restoring data. + */ + template + const basic_snapshot_loader & entities(Archive &archive) const { + typename traits_type::entity_type length{}; + + archive(length); + std::vector all(length); + + for(decltype(length) pos{}; pos < length; ++pos) { + archive(all[pos]); + } + + reg->assign(all.cbegin(), all.cend()); + + return *this; + } + + /** + * @brief Deprecated function. Currently, it does nothing. + * @tparam Archive Type of input archive. + * @return A valid loader to continue restoring data. + */ + template + [[deprecated("use ::entities instead, it imports now also destroyed entities")]] + const basic_snapshot_loader & destroyed(Archive &) const { return *this; } + + /** + * @brief Restores components and assigns them to the right entities. + * + * The template parameter list must be exactly the same used during + * serialization. In the event that the entity to which the component is + * assigned doesn't exist yet, the loader will take care to create it with + * the version it originally had. + * + * @tparam Component Types of components to restore. + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A valid loader to continue restoring data. + */ + template + const basic_snapshot_loader & component(Archive &archive) const { + (assign(archive), ...); + return *this; + } + + /** + * @brief Destroys those entities that have no components. + * + * In case all the entities were serialized but only part of the components + * was saved, it could happen that some of the entities have no components + * once restored.
+ * This functions helps to identify and destroy those entities. + * + * @return A valid loader to continue restoring data. + */ + const basic_snapshot_loader & orphans() const { + reg->orphans([this](const auto entt) { + reg->destroy(entt); + }); + + return *this; + } + +private: + basic_registry *reg; +}; + + +/** + * @brief Utility class for _continuous loading_. + * + * A _continuous loader_ is designed to load data from a source registry to a + * (possibly) non-empty destination. The loader can accommodate in a registry + * more than one snapshot in a sort of _continuous loading_ that updates the + * destination one step at a time.
+ * Identifiers that entities originally had are not transferred to the target. + * Instead, the loader maps remote identifiers to local ones while restoring a + * snapshot.
+ * An example of use is the implementation of a client-server applications with + * the requirement of transferring somehow parts of the representation side to + * side. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_continuous_loader { + using traits_type = entt_traits>; + + void destroy(Entity entt) { + const auto it = remloc.find(entt); + + if(it == remloc.cend()) { + const auto local = reg->create(); + remloc.emplace(entt, std::make_pair(local, true)); + reg->destroy(local); + } + } + + void restore(Entity entt) { + const auto it = remloc.find(entt); + + if(it == remloc.cend()) { + const auto local = reg->create(); + remloc.emplace(entt, std::make_pair(local, true)); + } else { + remloc[entt].first = reg->valid(remloc[entt].first) ? remloc[entt].first : reg->create(); + // set the dirty flag + remloc[entt].second = true; + } + } + + template + auto update(int, Container &container) + -> decltype(typename Container::mapped_type{}, void()) { + // map like container + Container other; + + for(auto &&pair: container) { + using first_type = std::remove_const_t::first_type>; + using second_type = typename std::decay_t::second_type; + + if constexpr(std::is_same_v && std::is_same_v) { + other.emplace(map(pair.first), map(pair.second)); + } else if constexpr(std::is_same_v) { + other.emplace(map(pair.first), std::move(pair.second)); + } else { + static_assert(std::is_same_v); + other.emplace(std::move(pair.first), map(pair.second)); + } + } + + std::swap(container, other); + } + + template + auto update(char, Container &container) + -> decltype(typename Container::value_type{}, void()) { + // vector like container + static_assert(std::is_same_v); + + for(auto &&entt: container) { + entt = map(entt); + } + } + + template + void update([[maybe_unused]] Other &instance, [[maybe_unused]] Member Type:: *member) { + if constexpr(!std::is_same_v) { + return; + } else if constexpr(std::is_same_v) { + instance.*member = map(instance.*member); + } else { + // maybe a container? let's try... + update(0, instance.*member); + } + } + + template + void remove_if_exists() { + for(auto &&ref: remloc) { + const auto local = ref.second.first; + + if(reg->valid(local)) { + reg->template remove_if_exists(local); + } + } + } + + template + void assign(Archive &archive, [[maybe_unused]] Member Type:: *... member) { + typename traits_type::entity_type length{}; + archive(length); + + while(length--) { + entity_type entt{}; + + if constexpr(std::is_empty_v) { + archive(entt); + restore(entt); + reg->template emplace_or_replace(map(entt)); + } else { + Other instance{}; + archive(entt, instance); + (update(instance, member), ...); + restore(entt); + reg->template emplace_or_replace(map(entt), std::as_const(instance)); + } + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_continuous_loader(basic_registry &source) ENTT_NOEXCEPT + : reg{&source} + {} + + /*! @brief Default move constructor. */ + basic_continuous_loader(basic_continuous_loader &&) = default; + + /*! @brief Default move assignment operator. @return This loader. */ + basic_continuous_loader & operator=(basic_continuous_loader &&) = default; + + /** + * @brief Restores entities that were in use during serialization. + * + * This function restores the entities that were in use during serialization + * and creates local counterparts for them if required. + * + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A non-const reference to this loader. + */ + template + basic_continuous_loader & entities(Archive &archive) { + typename traits_type::entity_type length{}; + entity_type entt{}; + + archive(length); + + for(decltype(length) pos{}; pos < length; ++pos) { + archive(entt); + + if(const auto entity = (to_integral(entt) & traits_type::entity_mask); entity == pos) { + restore(entt); + } else { + destroy(entt); + } + } + + return *this; + } + + /** + * @brief Deprecated function. Currently, it does nothing. + * @tparam Archive Type of input archive. + * @return A non-const reference to this loader. + */ + template + [[deprecated("use ::entities instead, it imports now also destroyed entities")]] + basic_continuous_loader & destroyed(Archive &) { return *this; } + + /** + * @brief Restores components and assigns them to the right entities. + * + * The template parameter list must be exactly the same used during + * serialization. In the event that the entity to which the component is + * assigned doesn't exist yet, the loader will take care to create a local + * counterpart for it.
+ * Members can be either data members of type entity_type or containers of + * entities. In both cases, the loader will visit them and update the + * entities by replacing each one with its local counterpart. + * + * @tparam Component Type of component to restore. + * @tparam Archive Type of input archive. + * @tparam Type Types of components to update with local counterparts. + * @tparam Member Types of members to update with their local counterparts. + * @param archive A valid reference to an input archive. + * @param member Members to update with their local counterparts. + * @return A non-const reference to this loader. + */ + template + basic_continuous_loader & component(Archive &archive, Member Type:: *... member) { + (remove_if_exists(), ...); + (assign(archive, member...), ...); + return *this; + } + + /** + * @brief Helps to purge entities that no longer have a conterpart. + * + * Users should invoke this member function after restoring each snapshot, + * unless they know exactly what they are doing. + * + * @return A non-const reference to this loader. + */ + basic_continuous_loader & shrink() { + auto it = remloc.begin(); + + while(it != remloc.cend()) { + const auto local = it->second.first; + bool &dirty = it->second.second; + + if(dirty) { + dirty = false; + ++it; + } else { + if(reg->valid(local)) { + reg->destroy(local); + } + + it = remloc.erase(it); + } + } + + return *this; + } + + /** + * @brief Destroys those entities that have no components. + * + * In case all the entities were serialized but only part of the components + * was saved, it could happen that some of the entities have no components + * once restored.
+ * This functions helps to identify and destroy those entities. + * + * @return A non-const reference to this loader. + */ + basic_continuous_loader & orphans() { + reg->orphans([this](const auto entt) { + reg->destroy(entt); + }); + + return *this; + } + + /** + * @brief Tests if a loader knows about a given entity. + * @param entt An entity identifier. + * @return True if `entity` is managed by the loader, false otherwise. + */ + bool contains(entity_type entt) const ENTT_NOEXCEPT { + return (remloc.find(entt) != remloc.cend()); + } + + /*! @copydoc contains */ + [[deprecated("use ::contains instead")]] + bool has(entity_type entt) const ENTT_NOEXCEPT { + return contains(entt); + } + + /** + * @brief Returns the identifier to which an entity refers. + * @param entt An entity identifier. + * @return The local identifier if any, the null entity otherwise. + */ + entity_type map(entity_type entt) const ENTT_NOEXCEPT { + const auto it = remloc.find(entt); + entity_type other = null; + + if(it != remloc.cend()) { + other = it->second.first; + } + + return other; + } + +private: + std::unordered_map> remloc; + basic_registry *reg; +}; + + +} + + +#endif + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + +// #include "view.hpp" +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief View. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +class basic_view; + + +/** + * @brief Multi component view. + * + * Multi component views iterate over those entities that have at least all the + * given components in their bags. During initialization, a multi component view + * looks at the number of entities available for each component and uses the + * smallest set in order to get a performance boost when iterate. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the view in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Views share references to the underlying data structures of the registry that + * generated them. Therefore any change to the entities and to the components + * made by means of the registry are immediately reflected by views. + * + * @warning + * Lifetime of a view must not overcome that of the registry that generated it. + * In any other case, attempting to use a view results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Exclude Types of components used to filter the view. + * @tparam Component Types of components iterated by the view. + */ +template +class basic_view, Component...> { + /*! @brief A registry is allowed to create views. */ + friend class basic_registry; + + template + using pool_type = std::conditional_t, const storage>, storage>; + + template + using component_iterator = decltype(std::declval>().begin()); + + using underlying_iterator = typename sparse_set::iterator; + using unchecked_type = std::array *, (sizeof...(Component) - 1)>; + using filter_type = std::array *, sizeof...(Exclude)>; + + class view_iterator final { + friend class basic_view, Component...>; + + view_iterator(const sparse_set &candidate, unchecked_type other, filter_type ignore, underlying_iterator curr) ENTT_NOEXCEPT + : view{&candidate}, + unchecked{other}, + filter{ignore}, + it{curr} + { + if(it != view->end() && !valid()) { + ++(*this); + } + } + + bool valid() const { + return std::all_of(unchecked.cbegin(), unchecked.cend(), [entt = *it](const sparse_set *curr) { return curr->contains(entt); }) + && std::none_of(filter.cbegin(), filter.cend(), [entt = *it](const sparse_set *curr) { return curr->contains(entt); }); + } + + public: + using difference_type = typename underlying_iterator::difference_type; + using value_type = typename underlying_iterator::value_type; + using pointer = typename underlying_iterator::pointer; + using reference = typename underlying_iterator::reference; + using iterator_category = std::bidirectional_iterator_tag; + + view_iterator() ENTT_NOEXCEPT = default; + + view_iterator & operator++() { + while(++it != view->end() && !valid()); + return *this; + } + + view_iterator operator++(int) { + view_iterator orig = *this; + return operator++(), orig; + } + + view_iterator & operator--() ENTT_NOEXCEPT { + while(--it != view->begin() && !valid()); + return *this; + } + + view_iterator operator--(int) ENTT_NOEXCEPT { + view_iterator orig = *this; + return operator--(), orig; + } + + bool operator==(const view_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + bool operator!=(const view_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + pointer operator->() const { + return it.operator->(); + } + + reference operator*() const { + return *operator->(); + } + + private: + const sparse_set *view; + unchecked_type unchecked; + filter_type filter; + underlying_iterator it; + }; + + // we could use pool_type &..., but vs complains about it and refuses to compile for unknown reasons (likely a bug) + basic_view(storage> &... component, storage> &... epool) ENTT_NOEXCEPT + : pools{&component..., &epool...} + {} + + const sparse_set & candidate() const ENTT_NOEXCEPT { + return *std::min({ static_cast *>(std::get *>(pools))... }, [](const auto *lhs, const auto *rhs) { + return lhs->size() < rhs->size(); + }); + } + + unchecked_type unchecked(const sparse_set &view) const { + std::size_t pos{}; + unchecked_type other{}; + ((std::get *>(pools) == &view ? nullptr : (other[pos++] = std::get *>(pools))), ...); + return other; + } + + template + decltype(auto) get([[maybe_unused]] component_iterator it, [[maybe_unused]] pool_type *cpool, [[maybe_unused]] const Entity entt) const { + if constexpr(std::is_same_v) { + return *it; + } else { + return cpool->get(entt); + } + } + + template + void traverse(Func func, type_list) const { + if constexpr(std::disjunction_v...>) { + auto it = std::get *>(pools)->begin(); + + for(const auto entt: static_cast &>(*std::get *>(pools))) { + auto curr = it++; + + if(((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) && (!std::get *>(pools)->contains(entt) && ...)) { + if constexpr(std::is_invocable_v({}))...>) { + func(get(curr, std::get *>(pools), entt)...); + } else { + func(entt, get(curr, std::get *>(pools), entt)...); + } + } + } + } else { + for(const auto entt: static_cast &>(*std::get *>(pools))) { + if(((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) && (!std::get *>(pools)->contains(entt) && ...)) { + if constexpr(std::is_invocable_v({}))...>) { + func(std::get *>(pools)->get(entt)...); + } else { + func(entt, std::get *>(pools)->get(entt)...); + } + } + } + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Input iterator type. */ + using iterator = view_iterator; + + /** + * @brief Returns the number of existing components of the given type. + * + * This isn't the number of entities iterated by the view. + * + * @tparam Comp Type of component of which to return the size. + * @return Number of existing components of the given type. + */ + template + size_type size() const ENTT_NOEXCEPT { + return std::get *>(pools)->size(); + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ + size_type size() const ENTT_NOEXCEPT { + return std::min({ std::get *>(pools)->size()... }); + } + + /** + * @brief Checks whether a view or some pools are empty. + * + * The view is definitely empty if one of the pools it uses is empty. In all + * other cases, the view may be empty and not return entities even if this + * function returns false. + * + * @tparam Comp Types of components in which one is interested. + * @return True if the view or the pools are empty, false otherwise. + */ + template + bool empty() const ENTT_NOEXCEPT { + if constexpr(sizeof...(Comp) == 0) { + return (std::get *>(pools)->empty() || ...); + } else { + return (std::get *>(pools)->empty() && ...); + } + } + + /** + * @brief Direct access to the list of components of a given pool. + * + * The returned pointer is such that range + * `[raw(), raw() + size()]` is always a valid range, even + * if the container is empty. + * + * @note + * There are no guarantees on the order of the components. Use `begin` and + * `end` if you want to iterate the view in the expected order. + * + * @tparam Comp Type of component in which one is interested. + * @return A pointer to the array of components. + */ + template + Comp * raw() const ENTT_NOEXCEPT { + return std::get *>(pools)->raw(); + } + + /** + * @brief Direct access to the list of entities of a given pool. + * + * The returned pointer is such that range + * `[data(), data() + size()]` is always a valid range, + * even if the container is empty. + * + * @note + * There are no guarantees on the order of the entities. Use `begin` and + * `end` if you want to iterate the view in the expected order. + * + * @tparam Comp Type of component in which one is interested. + * @return A pointer to the array of entities. + */ + template + const entity_type * data() const ENTT_NOEXCEPT { + return std::get *>(pools)->data(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * components. + * + * The returned iterator points to the first entity that has the given + * components. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given components. + */ + iterator begin() const { + const auto &view = candidate(); + const filter_type ignore{std::get *>(pools)...}; + return iterator{view, unchecked(view), ignore, view.begin()}; + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given components. + * + * The returned iterator points to the entity following the last entity that + * has the given components. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given components. + */ + iterator end() const { + const auto &view = candidate(); + const filter_type ignore{std::get *>(pools)...}; + return iterator{view, unchecked(view), ignore, view.end()}; + } + + /** + * @brief Returns the first entity that has the given components, if any. + * @return The first entity that has the given components if one exists, the + * null entity otherwise. + */ + entity_type front() const { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity that has the given components, if any. + * @return The last entity that has the given components if one exists, the + * null entity otherwise. + */ + entity_type back() const { + const auto it = std::make_reverse_iterator(end()); + return it != std::make_reverse_iterator(begin()) ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + iterator find(const entity_type entt) const { + const auto &view = candidate(); + const filter_type ignore{std::get *>(pools)...}; + iterator it{view, unchecked(view), ignore, view.find(entt)}; + return (it != end() && *it == entt) ? it : end(); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid entity identifier. + * @return True if the view contains the given entity, false otherwise. + */ + bool contains(const entity_type entt) const { + return (std::get *>(pools)->contains(entt) && ...) + && (!std::get *>(pools)->contains(entt) && ...); + } + + /** + * @brief Returns the components assigned to the given entity. + * + * Prefer this function instead of `registry::get` during iterations. It has + * far better performance than its counterpart. + * + * @warning + * Attempting to use an invalid component type results in a compilation + * error. Attempting to use an entity that doesn't belong to the view + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * view doesn't contain the given entity. + * + * @tparam Comp Types of components to get. + * @param entt A valid entity identifier. + * @return The components assigned to the entity. + */ + template + decltype(auto) get([[maybe_unused]] const entity_type entt) const { + ENTT_ASSERT(contains(entt)); + + if constexpr(sizeof...(Comp) == 0) { + static_assert(sizeof...(Component) == 1); + return (std::get *>(pools)->get(entt), ...); + } else if constexpr(sizeof...(Comp) == 1) { + return (std::get *>(pools)->get(entt), ...); + } else { + return std::tuple({}))...>{get(entt)...}; + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + const auto &view = candidate(); + ((std::get *>(pools) == &view ? each(std::move(func)) : void()), ...); + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The pool of the suggested component is used to lead the iterations. The + * returned entities will therefore respect the order of the pool associated + * with that type.
+ * It is no longer guaranteed that the performance is the best possible, but + * there will be greater control over the order of iteration. + * + * @sa each + * + * @tparam Comp Type of component to use to enforce the iteration order. + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + using non_empty_type = type_list_cat_t, type_list>...>; + traverse(std::move(func), non_empty_type{}); + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + [[deprecated("use ::each instead")]] + void less(Func func) const { + each(std::move(func)); + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The pool of the suggested component is used to lead the iterations. The + * returned entities will therefore respect the order of the pool associated + * with that type.
+ * It is no longer guaranteed that the performance is the best possible, but + * there will be greater control over the order of iteration. + * + * @sa less + * + * @tparam Comp Type of component to use to enforce the iteration order. + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + [[deprecated("use ::each instead")]] + void less(Func func) const { + each(std::move(func)); + } + +private: + const std::tuple *..., pool_type *...> pools; +}; + + +/** + * @brief Single component view specialization. + * + * Single component views are specialized in order to get a boost in terms of + * performance. This kind of views can access the underlying data structure + * directly and avoid superfluous checks. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given component are created and assigned to entities. + * * The entity currently pointed is modified (as an example, the given + * component is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pool iterated by the view in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Views share a reference to the underlying data structure of the registry that + * generated them. Therefore any change to the entities and to the components + * made by means of the registry are immediately reflected by views. + * + * @warning + * Lifetime of a view must not overcome that of the registry that generated it. + * In any other case, attempting to use a view results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Type of component iterated by the view. + */ +template +class basic_view, Component> { + /*! @brief A registry is allowed to create views. */ + friend class basic_registry; + + using pool_type = std::conditional_t, const storage>, storage>; + + basic_view(pool_type &ref) ENTT_NOEXCEPT + : pool{&ref} + {} + +public: + /*! @brief Type of component iterated by the view. */ + using raw_type = Component; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Input iterator type. */ + using iterator = typename sparse_set::iterator; + + /** + * @brief Returns the number of entities that have the given component. + * @return Number of entities that have the given component. + */ + size_type size() const ENTT_NOEXCEPT { + return pool->size(); + } + + /** + * @brief Checks whether a view is empty. + * @return True if the view is empty, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return pool->empty(); + } + + /** + * @brief Direct access to the list of components. + * + * The returned pointer is such that range `[raw(), raw() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the components. Use `begin` and + * `end` if you want to iterate the view in the expected order. + * + * @return A pointer to the array of components. + */ + raw_type * raw() const ENTT_NOEXCEPT { + return pool->raw(); + } + + /** + * @brief Direct access to the list of entities. + * + * The returned pointer is such that range `[data(), data() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the entities. Use `begin` and + * `end` if you want to iterate the view in the expected order. + * + * @return A pointer to the array of entities. + */ + const entity_type * data() const ENTT_NOEXCEPT { + return pool->data(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * component. + * + * The returned iterator points to the first entity that has the given + * component. If the view is empty, the returned iterator will be equal to + * `end()`. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the first entity that has the given component. + */ + iterator begin() const ENTT_NOEXCEPT { + return pool->sparse_set::begin(); + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given component. + * + * The returned iterator points to the entity following the last entity that + * has the given component. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed to the underlying data + * structures. + * + * @return An iterator to the entity following the last entity that has the + * given component. + */ + iterator end() const ENTT_NOEXCEPT { + return pool->sparse_set::end(); + } + + /** + * @brief Returns the first entity that has the given component, if any. + * @return The first entity that has the given component if one exists, the + * null entity otherwise. + */ + entity_type front() const { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity that has the given component, if any. + * @return The last entity that has the given component if one exists, the + * null entity otherwise. + */ + entity_type back() const { + const auto it = std::make_reverse_iterator(end()); + return it != std::make_reverse_iterator(begin()) ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid entity identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + iterator find(const entity_type entt) const { + const auto it = pool->find(entt); + return it != end() && *it == entt ? it : end(); + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid entity identifier. + * @return True if the view contains the given entity, false otherwise. + */ + bool contains(const entity_type entt) const { + return pool->contains(entt); + } + + /** + * @brief Returns the component assigned to the given entity. + * + * Prefer this function instead of `registry::get` during iterations. It has + * far better performance than its counterpart. + * + * @warning + * Attempting to use an entity that doesn't belong to the view results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * view doesn't contain the given entity. + * + * @param entt A valid entity identifier. + * @return The component assigned to the entity. + */ + template + decltype(auto) get(const entity_type entt) const { + static_assert(std::is_same_v); + ENTT_ASSERT(contains(entt)); + return pool->get(entt); + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a reference to the component if it's a non-empty one. + * The _constness_ of the component is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Component &); + * void(Component &); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + if constexpr(ENTT_IS_EMPTY(Component)) { + if constexpr(std::is_invocable_v) { + for(auto pos = pool->size(); pos; --pos) { + func(); + } + } else { + for(const auto entt: *this) { + func(entt); + } + } + } else { + if constexpr(std::is_invocable_v) { + for(auto &&component: *pool) { + func(component); + } + } else { + auto raw = pool->begin(); + + for(const auto entt: *this) { + func(entt, *(raw++)); + } + } + } + } + + /*! @copydoc each */ + template + [[deprecated("use ::each instead")]] + void less(Func func) const { + each(std::move(func)); + } + +private: + pool_type *pool; +}; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Fast and reliable entity-component system. + * + * The registry is the core class of the entity-component framework.
+ * It stores entities and arranges pools of components on a per request basis. + * By means of a registry, users can manage entities and components, then create + * views or groups to iterate them. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_registry { + using traits_type = entt_traits>; + + template + struct pool_handler final: storage { + static_assert(std::is_same_v>); + std::size_t super{}; + + auto on_construct() ENTT_NOEXCEPT { + return sink{construction}; + } + + auto on_update() ENTT_NOEXCEPT { + return sink{update}; + } + + auto on_destroy() ENTT_NOEXCEPT { + return sink{destruction}; + } + + template + decltype(auto) emplace(basic_registry &owner, const Entity entt, Args &&... args) { + storage::emplace(entt, std::forward(args)...); + construction.publish(owner, entt); + + if constexpr(!ENTT_IS_EMPTY(Component)) { + return this->get(entt); + } + } + + template + void insert(basic_registry &owner, It first, It last, Args &&... args) { + storage::insert(first, last, std::forward(args)...); + + if(!construction.empty()) { + while(first != last) { construction.publish(owner, *(first++)); } + } + } + + void remove(basic_registry &owner, const Entity entt) { + destruction.publish(owner, entt); + this->erase(entt); + } + + template + void remove(basic_registry &owner, It first, It last) { + if(std::distance(first, last) == std::distance(this->begin(), this->end())) { + if(!destruction.empty()) { + while(first != last) { destruction.publish(owner, *(first++)); } + } + + this->clear(); + } else { + while(first != last) { this->remove(owner, *(first++)); } + } + } + + template + decltype(auto) patch(basic_registry &owner, const Entity entt, Func &&... func) { + (std::forward(func)(this->get(entt)), ...); + update.publish(owner, entt); + + if constexpr(!ENTT_IS_EMPTY(Component)) { + return this->get(entt); + } + } + + decltype(auto) replace(basic_registry &owner, const Entity entt, [[maybe_unused]] Component component) { + if constexpr(ENTT_IS_EMPTY(Component)) { + return patch(owner, entt); + } else { + return patch(owner, entt, [&component](auto &&curr) { curr = std::move(component); }); + } + } + + private: + sigh construction{}; + sigh destruction{}; + sigh update{}; + }; + + struct pool_data { + id_type type_id{}; + std::unique_ptr> pool{}; + void(* remove)(sparse_set &, basic_registry &, const Entity){}; + }; + + template + struct group_handler; + + template + struct group_handler, get_t, Owned...> { + static_assert(std::conjunction_v>..., std::is_same>..., std::is_same>...>); + std::conditional_t, std::size_t> current{}; + + template + void maybe_valid_if(basic_registry &owner, const Entity entt) { + static_assert(std::disjunction_v>..., std::is_same>..., std::is_same>...>); + [[maybe_unused]] const auto cpools = std::forward_as_tuple(owner.assure()...); + + const auto is_valid = ((std::is_same_v || std::get &>(cpools).contains(entt)) && ...) + && ((std::is_same_v || owner.assure().contains(entt)) && ...) + && ((std::is_same_v || !owner.assure().contains(entt)) && ...); + + if constexpr(sizeof...(Owned) == 0) { + if(is_valid && !current.contains(entt)) { + current.emplace(entt); + } + } else { + if(is_valid && !(std::get<0>(cpools).index(entt) < current)) { + const auto pos = current++; + (std::get &>(cpools).swap(std::get &>(cpools).data()[pos], entt), ...); + } + } + } + + void discard_if([[maybe_unused]] basic_registry &owner, const Entity entt) { + if constexpr(sizeof...(Owned) == 0) { + if(current.contains(entt)) { + current.erase(entt); + } + } else { + if(const auto cpools = std::forward_as_tuple(owner.assure()...); std::get<0>(cpools).contains(entt) && (std::get<0>(cpools).index(entt) < current)) { + const auto pos = --current; + (std::get &>(cpools).swap(std::get &>(cpools).data()[pos], entt), ...); + } + } + } + }; + + struct group_data { + std::size_t size; + std::unique_ptr group; + bool (* owned)(const id_type) ENTT_NOEXCEPT; + bool (* get)(const id_type) ENTT_NOEXCEPT; + bool (* exclude)(const id_type) ENTT_NOEXCEPT; + }; + + struct variable_data { + id_type type_id; + std::unique_ptr value; + }; + + template + const pool_handler & assure() const { + static_assert(std::is_same_v>); + + if constexpr(has_type_index_v) { + const auto index = type_index::value(); + + if(!(index < pools.size())) { + pools.resize(index+1); + } + + if(auto &&pdata = pools[index]; !pdata.pool) { + pdata.type_id = type_info::id(); + pdata.pool.reset(new pool_handler()); + pdata.remove = [](sparse_set &cpool, basic_registry &owner, const entity_type entt) { + static_cast &>(cpool).remove(owner, entt); + }; + } + + return static_cast &>(*pools[index].pool); + } else { + sparse_set *candidate{nullptr}; + + if(auto it = std::find_if(pools.begin(), pools.end(), [id = type_info::id()](const auto &pdata) { return id == pdata.type_id; }); it == pools.cend()) { + candidate = pools.emplace_back(pool_data{ + type_info::id(), + std::unique_ptr>{new pool_handler()}, + [](sparse_set &cpool, basic_registry &owner, const entity_type entt) { + static_cast &>(cpool).remove(owner, entt); + } + }).pool.get(); + } else { + candidate = it->pool.get(); + } + + return static_cast &>(*candidate); + } + } + + template + pool_handler & assure() { + return const_cast &>(std::as_const(*this).template assure()); + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /*! @brief Default constructor. */ + basic_registry() = default; + + /*! @brief Default move constructor. */ + basic_registry(basic_registry &&) = default; + + /*! @brief Default move assignment operator. @return This registry. */ + basic_registry & operator=(basic_registry &&) = default; + + /** + * @brief Prepares a pool for the given type if required. + * @tparam Component Type of component for which to prepare a pool. + */ + template + void prepare() { + assure(); + } + + /** + * @brief Returns the number of existing components of the given type. + * @tparam Component Type of component of which to return the size. + * @return Number of existing components of the given type. + */ + template + size_type size() const { + return assure().size(); + } + + /** + * @brief Returns the number of entities created so far. + * @return Number of entities created so far. + */ + size_type size() const ENTT_NOEXCEPT { + return entities.size(); + } + + /** + * @brief Returns the number of entities still in use. + * @return Number of entities still in use. + */ + size_type alive() const { + auto sz = entities.size(); + auto curr = destroyed; + + for(; curr != null; --sz) { + curr = entities[to_integral(curr) & traits_type::entity_mask]; + } + + return sz; + } + + /** + * @brief Increases the capacity of the registry or of the pools for the + * given components. + * + * If no components are specified, the capacity of the registry is + * increased, that is the number of entities it contains. Otherwise the + * capacity of the pools for the given components is increased.
+ * In both cases, if the new capacity is greater than the current capacity, + * new storage is allocated, otherwise the method does nothing. + * + * @tparam Component Types of components for which to reserve storage. + * @param cap Desired capacity. + */ + template + void reserve(const size_type cap) { + if constexpr(sizeof...(Component) == 0) { + entities.reserve(cap); + } else { + (assure().reserve(cap), ...); + } + } + + /** + * @brief Returns the capacity of the pool for the given component. + * @tparam Component Type of component in which one is interested. + * @return Capacity of the pool of the given component. + */ + template + size_type capacity() const { + return assure().capacity(); + } + + /** + * @brief Returns the number of entities that a registry has currently + * allocated space for. + * @return Capacity of the registry. + */ + size_type capacity() const ENTT_NOEXCEPT { + return entities.capacity(); + } + + /** + * @brief Requests the removal of unused capacity for the given components. + * @tparam Component Types of components for which to reclaim unused + * capacity. + */ + template + void shrink_to_fit() { + (assure().shrink_to_fit(), ...); + } + + /** + * @brief Checks whether the registry or the pools of the given components + * are empty. + * + * A registry is considered empty when it doesn't contain entities that are + * still in use. + * + * @tparam Component Types of components in which one is interested. + * @return True if the registry or the pools of the given components are + * empty, false otherwise. + */ + template + bool empty() const { + if constexpr(sizeof...(Component) == 0) { + return !alive(); + } else { + return (assure().empty() && ...); + } + } + + /** + * @brief Direct access to the list of components of a given pool. + * + * The returned pointer is such that range + * `[raw(), raw() + size()]` is always a + * valid range, even if the container is empty. + * + * There are no guarantees on the order of the components. Use a view if you + * want to iterate entities and components in the expected order. + * + * @note + * Empty components aren't explicitly instantiated. Therefore, this function + * isn't available for them. A compilation error will occur if invoked. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of components of the given type. + */ + template + const Component * raw() const { + return assure().raw(); + } + + /*! @copydoc raw */ + template + Component * raw() { + return const_cast(std::as_const(*this).template raw()); + } + + /** + * @brief Direct access to the list of entities of a given pool. + * + * The returned pointer is such that range + * `[data(), data() + size()]` is always a + * valid range, even if the container is empty. + * + * There are no guarantees on the order of the entities. Use a view if you + * want to iterate entities and components in the expected order. + * + * @tparam Component Type of component in which one is interested. + * @return A pointer to the array of entities. + */ + template + const entity_type * data() const { + return assure().data(); + } + + /** + * @brief Direct access to the list of entities of a registry. + * + * The returned pointer is such that range `[data(), data() + size()]` is + * always a valid range, even if the container is empty. + * + * @warning + * This list contains both valid and destroyed entities and isn't suitable + * for direct use. + * + * @return A pointer to the array of entities. + */ + const entity_type * data() const ENTT_NOEXCEPT { + return entities.data(); + } + + /** + * @brief Checks if an entity identifier refers to a valid entity. + * @param entity An entity identifier, either valid or not. + * @return True if the identifier is valid, false otherwise. + */ + bool valid(const entity_type entity) const { + const auto pos = size_type(to_integral(entity) & traits_type::entity_mask); + return (pos < entities.size() && entities[pos] == entity); + } + + /** + * @brief Returns the entity identifier without the version. + * @param entity An entity identifier, either valid or not. + * @return The entity identifier without the version. + */ + static entity_type entity(const entity_type entity) ENTT_NOEXCEPT { + return entity_type{to_integral(entity) & traits_type::entity_mask}; + } + + /** + * @brief Returns the version stored along with an entity identifier. + * @param entity An entity identifier, either valid or not. + * @return The version stored along with the given entity identifier. + */ + static version_type version(const entity_type entity) ENTT_NOEXCEPT { + return version_type(to_integral(entity) >> traits_type::entity_shift); + } + + /** + * @brief Returns the actual version for an entity identifier. + * + * @warning + * Attempting to use an entity that doesn't belong to the registry results + * in undefined behavior. An entity belongs to the registry even if it has + * been previously destroyed and/or recycled.
+ * An assertion will abort the execution at runtime in debug mode if the + * registry doesn't own the given entity. + * + * @param entity A valid entity identifier. + * @return Actual version for the given entity identifier. + */ + version_type current(const entity_type entity) const { + const auto pos = size_type(to_integral(entity) & traits_type::entity_mask); + ENTT_ASSERT(pos < entities.size()); + return version_type(to_integral(entities[pos]) >> traits_type::entity_shift); + } + + /** + * @brief Creates a new entity and returns it. + * + * There are two kinds of possible entity identifiers: + * + * * Newly created ones in case no entities have been previously destroyed. + * * Recycled ones with updated versions. + * + * @return A valid entity identifier. + */ + entity_type create() { + entity_type entt; + + if(destroyed == null) { + entt = entities.emplace_back(entity_type(entities.size())); + // traits_type::entity_mask is reserved to allow for null identifiers + ENTT_ASSERT(to_integral(entt) < traits_type::entity_mask); + } else { + const auto curr = to_integral(destroyed); + const auto version = to_integral(entities[curr]) & (traits_type::version_mask << traits_type::entity_shift); + destroyed = entity_type{to_integral(entities[curr]) & traits_type::entity_mask}; + entt = entities[curr] = entity_type{curr | version}; + } + + return entt; + } + + /** + * @brief Creates a new entity and returns it. + * + * @sa create + * + * If the requested entity isn't in use, the suggested identifier is created + * and returned. Otherwise, a new one will be generated for this purpose. + * + * @param hint A desired entity identifier. + * @return A valid entity identifier. + */ + entity_type create(const entity_type hint) { + ENTT_ASSERT(hint != null); + entity_type entt; + + if(const auto req = (to_integral(hint) & traits_type::entity_mask); !(req < entities.size())) { + entities.reserve(req + 1); + + for(auto pos = entities.size(); pos < req; ++pos) { + entities.emplace_back(destroyed); + destroyed = entity_type(pos); + } + + entt = entities.emplace_back(hint); + } else if(const auto curr = (to_integral(entities[req]) & traits_type::entity_mask); req == curr) { + entt = create(); + } else { + auto *it = &destroyed; + for(; (to_integral(*it) & traits_type::entity_mask) != req; it = &entities[to_integral(*it) & traits_type::entity_mask]); + *it = entity_type{curr | (to_integral(*it) & (traits_type::version_mask << traits_type::entity_shift))}; + entt = entities[req] = hint; + } + + return entt; + } + + /** + * @brief Assigns each element in a range an entity. + * + * @sa create + * + * @tparam It Type of forward iterator. + * @param first An iterator to the first element of the range to generate. + * @param last An iterator past the last element of the range to generate. + */ + template + void create(It first, It last) { + std::generate(first, last, [this]() { return create(); }); + } + + /** + * @brief Assigns entities to an empty registry. + * + * This function is intended for use in conjunction with `raw`.
+ * Don't try to inject ranges of randomly generated entities. There is no + * guarantee that a registry will continue to work properly in this case. + * + * @warning + * An assertion will abort the execution at runtime in debug mode if all + * pools aren't empty. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void assign(It first, It last) { + ENTT_ASSERT(std::all_of(pools.cbegin(), pools.cend(), [](auto &&pdata) { return !pdata.pool || pdata.pool->empty(); })); + entities.assign(first, last); + destroyed = null; + + for(std::size_t pos{}, end = entities.size(); pos < end; ++pos) { + if((to_integral(entities[pos]) & traits_type::entity_mask) != pos) { + const auto version = to_integral(entities[pos]) & (traits_type::version_mask << traits_type::entity_shift); + entities[pos] = entity_type{to_integral(destroyed) | version}; + destroyed = entity_type(pos); + } + } + } + + /** + * @brief Destroys an entity. + * + * When an entity is destroyed, its version is updated and the identifier + * can be recycled at any time. + * + * @sa remove_all + * + * @param entity A valid entity identifier. + */ + void destroy(const entity_type entity) { + destroy(entity, (to_integral(entity) >> traits_type::entity_shift) + 1); + } + + /** + * @brief Destroys an entity. + * + * If the entity isn't already destroyed, the suggested version is used + * instead of the implicitly generated one. + * + * @sa remove_all + * + * @param entity A valid entity identifier. + * @param version A desired version upon destruction. + */ + void destroy(const entity_type entity, const version_type version) { + remove_all(entity); + // lengthens the implicit list of destroyed entities + const auto entt = to_integral(entity) & traits_type::entity_mask; + entities[entt] = entity_type{to_integral(destroyed) | (version << traits_type::entity_shift)}; + destroyed = entity_type{entt}; + } + + /** + * @brief Destroys all the entities in a range. + * + * @sa destroy + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void destroy(It first, It last) { + while(first != last) { destroy(*(first++)); } + } + + /** + * @brief Assigns the given component to an entity. + * + * A new instance of the given component is created and initialized with the + * arguments provided (the component must have a proper constructor or be of + * aggregate type). Then the component is assigned to the given entity. + * + * @warning + * Attempting to use an invalid entity or to assign a component to an entity + * that already owns it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity already owns an instance of the given + * component. + * + * @tparam Component Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace(const entity_type entity, Args &&... args) { + ENTT_ASSERT(valid(entity)); + return assure().emplace(*this, entity, std::forward(args)...); + } + + /*! @copydoc emplace */ + template + [[deprecated("use ::emplace instead")]] + decltype(auto) assign(const entity_type entity, Args &&... args) { + return emplace(entity, std::forward(args)...); + } + + /** + * @brief Assigns each entity in a range the given component. + * + * @sa emplace + * + * @tparam Component Type of component to create. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the component to assign. + */ + template + void insert(It first, It last, const Component &value = {}) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); + assure().insert(*this, first, last, value); + } + + /*! @copydoc insert */ + template + [[deprecated("use ::insert instead")]] + std::enable_if_t::value_type, entity_type>, void> + assign(It first, It last, const Component &value = {}) { + return insert(std::move(first), std::move(last), value); + } + + /** + * @brief Assigns each entity in a range the given components. + * + * @sa emplace + * + * @tparam Component Type of component to create. + * @tparam EIt Type of input iterator. + * @tparam CIt Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param from An iterator to the first element of the range of components. + * @param to An iterator past the last element of the range of components. + */ + template + void insert(EIt first, EIt last, CIt from, CIt to) { + static_assert(std::is_constructible_v::value_type>); + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); + assure().insert(*this, first, last, from, to); + } + + /*! @copydoc insert */ + template + [[deprecated("use ::insert instead")]] + std::enable_if_t::value_type, entity_type>, void> + assign(EIt first, EIt last, CIt value) { + return insert(std::move(first), std::move(last), value, value + std::distance(first, last)); + } + + /** + * @brief Assigns or replaces the given component for an entity. + * + * Equivalent to the following snippet (pseudocode): + * + * @code{.cpp} + * auto &component = registry.has(entity) ? registry.replace(entity, args...) : registry.emplace(entity, args...); + * @endcode + * + * Prefer this function anyway because it has slightly better performance. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace_or_replace(const entity_type entity, Args &&... args) { + ENTT_ASSERT(valid(entity)); + auto &cpool = assure(); + + return cpool.contains(entity) + ? cpool.replace(*this, entity, Component{std::forward(args)...}) + : cpool.emplace(*this, entity, std::forward(args)...); + } + + /*! @copydoc emplace_or_replace */ + template + [[deprecated("use ::emplace_or_replace instead")]] + decltype(auto) assign_or_replace(const entity_type entity, Args &&... args) { + return emplace_or_replace(entity, std::forward(args)...); + } + + /** + * @brief Patches the given component for an entity. + * + * The signature of the functions 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.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't own an instance of the given + * component. + * + * @tparam Component Type of component to patch. + * @tparam Func Types of the function objects to invoke. + * @param entity A valid entity identifier. + * @param func Valid function objects. + * @return A reference to the patched component. + */ + template + decltype(auto) patch(const entity_type entity, Func &&... func) { + ENTT_ASSERT(valid(entity)); + return assure().patch(*this, entity, std::forward(func)...); + } + + /*! @copydoc patch */ + template + [[deprecated("use registry::patch instead")]] + auto replace(const entity_type entity, Func &&... func) + -> decltype((func(std::declval()), ...), assign(entity)) { + return patch(entity, std::forward(func)...); + } + + /** + * @brief Replaces the given component for an entity. + * + * A new instance of the given component is created and initialized with the + * arguments provided (the component must have a proper constructor or be of + * aggregate type). Then the component is assigned to the given entity. + * + * @warning + * Attempting to use an invalid entity or to replace a component of an + * entity that doesn't own it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't own an instance of the given + * component. + * + * @tparam Component Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the component being replaced. + */ + template + auto replace(const entity_type entity, Args &&... args) + -> decltype(std::enable_if_t(), Component{std::forward(args)...}, assure().get(entity)) { + return assure().replace(*this, entity, Component{std::forward(args)...}); + } + + /** + * @brief Removes the given components from an entity. + * + * @warning + * Attempting to use an invalid entity or to remove a component from an + * entity that doesn't own it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't own an instance of the given + * component. + * + * @tparam Component Types of components to remove. + * @param entity A valid entity identifier. + */ + template + void remove(const entity_type entity) { + ENTT_ASSERT(valid(entity)); + (assure().remove(*this, entity), ...); + } + + /** + * @brief Removes the given components from all the entities in a range. + * + * @see remove + * + * @tparam Component Types of components to remove. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void remove(It first, It last) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); + (assure().remove(*this, first, last), ...); + } + + /** + * @brief Removes the given components from an entity. + * + * Equivalent to the following snippet (pseudocode): + * + * @code{.cpp} + * if(registry.has(entity)) { registry.remove(entity) } + * @endcode + * + * Prefer this function anyway because it has slightly better performance. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Types of components to remove. + * @param entity A valid entity identifier. + */ + template + void remove_if_exists(const entity_type entity) { + ENTT_ASSERT(valid(entity)); + + ([this, entity](auto &&cpool) { + if(cpool.contains(entity)) { + cpool.remove(*this, entity); + } + }(assure()), ...); + } + + /** + * @brief Removes all the components from an entity and makes it orphaned. + * + * @warning + * In case there are listeners that observe the destruction of components + * and assign other components to the entity in their bodies, the result of + * invoking this function may not be as expected. In the worst case, it + * could lead to undefined behavior. An assertion will abort the execution + * at runtime in debug mode if a violation is detected. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @param entity A valid entity identifier. + */ + void remove_all(const entity_type entity) { + ENTT_ASSERT(valid(entity)); + + for(auto pos = pools.size(); pos; --pos) { + if(auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->contains(entity)) { + pdata.remove(*pdata.pool, *this, entity); + } + } + } + + /** + * @brief Checks if an entity has all the given components. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Components for which to perform the check. + * @param entity A valid entity identifier. + * @return True if the entity has all the components, false otherwise. + */ + template + bool has(const entity_type entity) const { + ENTT_ASSERT(valid(entity)); + return (assure().contains(entity) && ...); + } + + /** + * @brief Checks if an entity has at least one of the given components. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Components for which to perform the check. + * @param entity A valid entity identifier. + * @return True if the entity has at least one of the given components, + * false otherwise. + */ + template + bool any(const entity_type entity) const { + ENTT_ASSERT(valid(entity)); + return (assure().contains(entity) || ...); + } + + /** + * @brief Returns references to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity or to get a component from an entity + * that doesn't own it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't own an instance of the given + * component. + * + * @tparam Component Types of components to get. + * @param entity A valid entity identifier. + * @return References to the components owned by the entity. + */ + template + decltype(auto) get([[maybe_unused]] const entity_type entity) const { + ENTT_ASSERT(valid(entity)); + + if constexpr(sizeof...(Component) == 1) { + return (assure().get(entity), ...); + } else { + return std::forward_as_tuple(get(entity)...); + } + } + + /*! @copydoc get */ + template + decltype(auto) get([[maybe_unused]] const entity_type entity) { + ENTT_ASSERT(valid(entity)); + + if constexpr(sizeof...(Component) == 1) { + return (assure().get(entity), ...); + } else { + return std::forward_as_tuple(get(entity)...); + } + } + + /** + * @brief Returns a reference to the given component for an entity. + * + * In case the entity doesn't own the component, the parameters provided are + * used to construct it.
+ * Equivalent to the following snippet (pseudocode): + * + * @code{.cpp} + * auto &component = registry.has(entity) ? registry.get(entity) : registry.emplace(entity, args...); + * @endcode + * + * Prefer this function anyway because it has slightly better performance. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Type of component to get. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the component. + * @return Reference to the component owned by the entity. + */ + template + decltype(auto) get_or_emplace(const entity_type entity, Args &&... args) { + ENTT_ASSERT(valid(entity)); + auto &cpool = assure(); + return cpool.contains(entity) ? cpool.get(entity) : cpool.emplace(*this, entity, std::forward(args)...); + } + + /*! @copydoc get_or_emplace */ + template + [[deprecated("use ::get_or_emplace instead")]] + decltype(auto) get_or_assign(const entity_type entity, Args &&... args) { + return get_or_emplace(entity, std::forward(args)...); + } + + /** + * @brief Returns pointers to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Types of components to get. + * @param entity A valid entity identifier. + * @return Pointers to the components owned by the entity. + */ + template + auto try_get([[maybe_unused]] const entity_type entity) const { + ENTT_ASSERT(valid(entity)); + + if constexpr(sizeof...(Component) == 1) { + return (assure().try_get(entity), ...); + } else { + return std::make_tuple(try_get(entity)...); + } + } + + /*! @copydoc try_get */ + template + auto try_get([[maybe_unused]] const entity_type entity) { + ENTT_ASSERT(valid(entity)); + + if constexpr(sizeof...(Component) == 1) { + return (assure().try_get(entity), ...); + } else { + return std::make_tuple(try_get(entity)...); + } + } + + /** + * @brief Clears a whole registry or the pools for the given components. + * @tparam Component Types of components to remove from their entities. + */ + template + void clear() { + if constexpr(sizeof...(Component) == 0) { + // useless this-> used to suppress a warning with clang + each([this](const auto entity) { this->destroy(entity); }); + } else { + ([this](auto &&cpool) { + cpool.remove(*this, cpool.sparse_set::begin(), cpool.sparse_set::end()); + }(assure()), ...); + } + } + + /** + * @brief Iterates all the entities that are still in use. + * + * The function object is invoked for each entity that is still in use.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const Entity); + * @endcode + * + * This function is fairly slow and should not be used frequently. However, + * it's useful for iterating all the entities still in use, regardless of + * their components. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + static_assert(std::is_invocable_v); + + if(destroyed == null) { + for(auto pos = entities.size(); pos; --pos) { + func(entities[pos-1]); + } + } else { + for(auto pos = entities.size(); pos; --pos) { + if(const auto entt = entities[pos - 1]; (to_integral(entt) & traits_type::entity_mask) == (pos - 1)) { + func(entt); + } + } + } + } + + /** + * @brief Checks if an entity has components assigned. + * @param entity A valid entity identifier. + * @return True if the entity has no components assigned, false otherwise. + */ + bool orphan(const entity_type entity) const { + ENTT_ASSERT(valid(entity)); + return std::none_of(pools.cbegin(), pools.cend(), [entity](auto &&pdata) { return pdata.pool && pdata.pool->contains(entity); }); + } + + /** + * @brief Iterates orphans and applies them the given function object. + * + * The function object is invoked for each entity that is still in use and + * has no components assigned.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const Entity); + * @endcode + * + * This function can be very slow and should not be used frequently. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void orphans(Func func) const { + static_assert(std::is_invocable_v); + + each([this, &func](const auto entity) { + if(orphan(entity)) { + func(entity); + } + }); + } + + /** + * @brief Returns a sink object for the given component. + * + * A sink is an opaque object used to connect listeners to components.
+ * The sink returned by this function can be used to receive notifications + * whenever a new instance of the given component is created and assigned to + * an entity. + * + * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(registry &, Entity); + * @endcode + * + * Listeners are invoked **after** the component has been assigned to the + * entity. + * + * @sa sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + auto on_construct() { + return assure().on_construct(); + } + + /** + * @brief Returns a sink object for the given component. + * + * A sink is an opaque object used to connect listeners to components.
+ * The sink returned by this function can be used to receive notifications + * whenever an instance of the given component is explicitly updated. + * + * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(registry &, Entity); + * @endcode + * + * Listeners are invoked **after** the component has been updated. + * + * @sa sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + auto on_update() { + return assure().on_update(); + } + + /*! @copydoc on_update */ + template + [[deprecated("use registry::on_update instead")]] + auto on_replace() { + return on_update(); + } + + /** + * @brief Returns a sink object for the given component. + * + * A sink is an opaque object used to connect listeners to components.
+ * The sink returned by this function can be used to receive notifications + * whenever an instance of the given component is removed from an entity and + * thus destroyed. + * + * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(registry &, Entity); + * @endcode + * + * Listeners are invoked **before** the component has been removed from the + * entity. + * + * @sa sink + * + * @tparam Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + auto on_destroy() { + return assure().on_destroy(); + } + + /** + * @brief Sorts the pool of entities for the given component. + * + * The order of the elements in a pool is highly affected by assignments + * of components to entities and deletions. Components are arranged to + * maximize the performance during iterations and users should not make any + * assumption on the order.
+ * This function can be used to impose an order to the elements in the pool + * of the given component. The order is kept valid until a component of the + * given type is assigned or removed from an entity. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * bool(const Component &, const Component &); + * @endcode + * + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * The comparison funtion object received by the sort function object hasn't + * necessarily the type of the one passed along with the other parameters to + * this member function. + * + * @warning + * Pools of components owned by a group cannot be sorted.
+ * An assertion will abort the execution at runtime in debug mode in case + * the pool is owned by a group. + * + * @tparam Component Type of components to sort. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { + auto &cpool = assure(); + ENTT_ASSERT(!cpool.super); + cpool.sort(cpool.begin(), cpool.end(), std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sorts two pools of components in the same way. + * + * The order of the elements in a pool is highly affected by assignments + * of components to entities and deletions. Components are arranged to + * maximize the performance during iterations and users should not make any + * assumption on the order. + * + * It happens that different pools of components must be sorted the same way + * because of runtime and/or performance constraints. This function can be + * used to order a pool of components according to the order between the + * entities in another pool of components. + * + * @b How @b it @b works + * + * Being `A` and `B` the two sets where `B` is the master (the one the order + * of which rules) and `A` is the slave (the one to sort), after a call to + * this function an iterator for `A` will return the entities according to + * the following rules: + * + * * All the entities in `A` that are also in `B` are returned first + * according to the order they have in `B`. + * * All the entities in `A` that are not in `B` are returned in no + * particular order after all the other entities. + * + * Any subsequent change to `B` won't affect the order in `A`. + * + * @warning + * Pools of components owned by a group cannot be sorted.
+ * An assertion will abort the execution at runtime in debug mode in case + * the pool is owned by a group. + * + * @tparam To Type of components to sort. + * @tparam From Type of components to use to sort. + */ + template + void sort() { + auto &cpool = assure(); + ENTT_ASSERT(!cpool.super); + cpool.respect(assure()); + } + + /** + * @brief Returns a view for the given components. + * + * This kind of objects are created on the fly and share with the registry + * its internal data structures.
+ * Feel free to discard a view after the use. Creating and destroying a view + * is an incredibly cheap operation because they do not require any type of + * initialization.
+ * As a rule of thumb, storing a view should never be an option. + * + * Views do their best to iterate the smallest set of candidate entities. + * In particular: + * + * * Single component views are incredibly fast and iterate a packed array + * of entities, all of which has the given component. + * * Multi component views look at the number of entities available for each + * component and pick up a reference to the smallest set of candidates to + * test for the given components. + * + * Views in no way affect the functionalities of the registry nor those of + * the underlying pools. + * + * @note + * Multi component views are pretty fast. However their performance tend to + * degenerate when the number of components to iterate grows up and the most + * of the entities have all the given components.
+ * To get a performance boost, consider using a group instead. + * + * @tparam Component Type of components used to construct the view. + * @tparam Exclude Types of components used to filter the view. + * @return A newly created view. + */ + template + entt::basic_view, Component...> view(exclude_t = {}) { + static_assert(sizeof...(Component) > 0); + return { assure>()..., assure()... }; + } + + /*! @copydoc view */ + template + entt::basic_view, Component...> view(exclude_t = {}) const { + static_assert(std::conjunction_v...>); + return const_cast(this)->view(exclude); + } + + /** + * @brief Checks whether the given components belong to any group. + * @tparam Component Types of components in which one is interested. + * @return True if the pools of the given components are sortable, false + * otherwise. + */ + template + bool sortable() const { + return !(assure().super || ...); + } + + /** + * @brief Returns a group for the given components. + * + * This kind of objects are created on the fly and share with the registry + * its internal data structures.
+ * Feel free to discard a group after the use. Creating and destroying a + * group is an incredibly cheap operation because they do not require any + * type of initialization, but for the first time they are requested.
+ * As a rule of thumb, storing a group should never be an option. + * + * Groups support exclusion lists and can own types of components. The more + * types are owned by a group, the faster it is to iterate entities and + * components.
+ * However, groups also affect some features of the registry such as the + * creation and destruction of components, which will consequently be + * slightly slower (nothing that can be noticed in most cases). + * + * @note + * Pools of components that are owned by a group cannot be sorted anymore. + * The group takes the ownership of the pools and arrange components so as + * to iterate them as fast as possible. + * + * @tparam Owned Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @return A newly created group. + */ + template + entt::basic_group, get_t, Owned...> group(get_t, exclude_t = {}) { + static_assert(sizeof...(Owned) + sizeof...(Get) > 0); + static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1); + + using handler_type = group_handler, get_t...>, std::decay_t...>; + + const auto cpools = std::forward_as_tuple(assure>()..., assure>()...); + constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); + handler_type *handler = nullptr; + + if(auto it = std::find_if(groups.cbegin(), groups.cend(), [size](const auto &gdata) { + return gdata.size == size + && (gdata.owned(type_info>::id()) && ...) + && (gdata.get(type_info>::id()) && ...) + && (gdata.exclude(type_info::id()) && ...); + }); it != groups.cend()) + { + handler = static_cast(it->group.get()); + } + + if(!handler) { + group_data candidate = { + size, + { new handler_type{}, [](void *instance) { delete static_cast(instance); } }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_info>::id()) || ...); }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_info>::id()) || ...); }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_info::id()) || ...); }, + }; + + handler = static_cast(candidate.group.get()); + + const void *maybe_valid_if = nullptr; + const void *discard_if = nullptr; + + if constexpr(sizeof...(Owned) == 0) { + groups.push_back(std::move(candidate)); + } else { + ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [size](const auto &gdata) { + const auto overlapping = (0u + ... + gdata.owned(type_info>::id())); + const auto sz = overlapping + (0u + ... + gdata.get(type_info>::id())) + (0u + ... + gdata.exclude(type_info::id())); + return !overlapping || ((sz == size) || (sz == gdata.size)); + })); + + const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [size](const auto &gdata) { + return !(0u + ... + gdata.owned(type_info>::id())) || (size > (gdata.size)); + }); + + const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto &gdata) { + return (0u + ... + gdata.owned(type_info>::id())); + }); + + maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get()); + discard_if = (prev == groups.crend() ? discard_if : prev->group.get()); + groups.insert(next, std::move(candidate)); + } + + ((std::get> &>(cpools).super = std::max(std::get> &>(cpools).super, size)), ...); + + (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); + (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); + (on_destroy().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>(*handler), ...); + + (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + (on_construct().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + + if constexpr(sizeof...(Owned) == 0) { + for(const auto entity: view(entt::exclude)) { + handler->current.emplace(entity); + } + } else { + // we cannot iterate backwards because we want to leave behind valid entities in case of owned types + for(auto *first = std::get<0>(cpools).data(), *last = first + std::get<0>(cpools).size(); first != last; ++first) { + handler->template maybe_valid_if...>>>(*this, *first); + } + } + } + + if constexpr(sizeof...(Owned) == 0) { + return { handler->current, std::get> &>(cpools)... }; + } else { + return { std::get<0>(cpools).super, handler->current, std::get> &>(cpools)... , std::get> &>(cpools)... }; + } + } + + /** + * @brief Returns a group for the given components. + * + * @sa group + * + * @tparam Owned Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @return A newly created group. + */ + template + entt::basic_group, get_t, Owned...> group(get_t, exclude_t = {}) const { + static_assert(std::conjunction_v..., std::is_const...>); + return const_cast(this)->group(entt::get, exclude); + } + + /** + * @brief Returns a group for the given components. + * + * @sa group + * + * @tparam Owned Types of components owned by the group. + * @tparam Exclude Types of components used to filter the group. + * @return A newly created group. + */ + template + entt::basic_group, get_t<>, Owned...> group(exclude_t = {}) { + return group(entt::get<>, exclude); + } + + /** + * @brief Returns a group for the given components. + * + * @sa group + * + * @tparam Owned Types of components owned by the group. + * @tparam Exclude Types of components used to filter the group. + * @return A newly created group. + */ + template + entt::basic_group, get_t<>, Owned...> group(exclude_t = {}) const { + static_assert(std::conjunction_v...>); + return const_cast(this)->group(exclude); + } + + /** + * @brief Returns a runtime view for the given components. + * + * This kind of objects are created on the fly and share with the registry + * its internal data structures.
+ * Users should throw away the view after use. Fortunately, creating and + * destroying a runtime view is an incredibly cheap operation because they + * do not require any type of initialization.
+ * As a rule of thumb, storing a view should never be an option. + * + * Runtime views are to be used when users want to construct a view from + * some external inputs and don't know at compile-time what are the required + * components.
+ * This is particularly well suited to plugin systems and mods in general. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of components. + * @param last An iterator past the last element of the range of components. + * @return A newly created runtime view. + */ + template + entt::basic_runtime_view runtime_view(It first, It last) const { + std::vector *> selected(std::distance(first, last)); + + std::transform(first, last, selected.begin(), [this](const auto ctype) { + const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto &&pdata) { return pdata.pool && pdata.type_id == ctype; }); + return it == pools.cend() ? nullptr : it->pool.get(); + }); + + return { std::move(selected) }; + } + + /** + * @brief Returns a temporary object to use to create snapshots. + * + * A snapshot is either a full or a partial dump of a registry.
+ * It can be used to save and restore its internal state or to keep two or + * more instances of this class in sync, as an example in a client-server + * architecture. + * + * @return A temporary object to use to take snasphosts. + */ + [[deprecated("basic_snapshot has now a constructor that accepts a reference to a registry")]] + entt::basic_snapshot snapshot() const { + return { *this }; + } + + /** + * @brief Returns a temporary object to use to load snapshots. + * + * A snapshot is either a full or a partial dump of a registry.
+ * It can be used to save and restore its internal state or to keep two or + * more instances of this class in sync, as an example in a client-server + * architecture. + * + * @note + * The loader returned by this function requires that the registry be empty. + * In case it isn't, all the data will be automatically deleted before to + * return. + * + * @return A temporary object to use to load snasphosts. + */ + [[deprecated("basic_snapshot_loader has now a constructor that accepts a reference to a registry")]] + basic_snapshot_loader loader() { + return { *this }; + } + + /** + * @brief Visits an entity and returns the types for its components. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const id_type); + * @endcode + * + * Returned identifiers are those of the components owned by the entity. + * + * @sa type_info + * + * @warning + * It's not specified whether a component attached to or removed from the + * given entity during the visit is returned or not to the caller. + * + * @tparam Func Type of the function object to invoke. + * @param entity A valid entity identifier. + * @param func A valid function object. + */ + template + void visit(entity_type entity, Func func) const { + for(auto pos = pools.size(); pos; --pos) { + if(const auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->contains(entity)) { + func(pdata.type_id); + } + } + } + + /** + * @brief Visits a registry and returns the types for its components. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const id_type); + * @endcode + * + * Returned identifiers are those of the components managed by the registry. + * + * @sa type_info + * + * @warning + * It's not specified whether a component for which a pool is created during + * the visit is returned or not to the caller. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void visit(Func func) const { + for(auto pos = pools.size(); pos; --pos) { + if(const auto &pdata = pools[pos-1]; pdata.pool) { + func(pdata.type_id); + } + } + } + + /** + * @brief Binds an object to the context of the registry. + * + * If the value already exists it is overwritten, otherwise a new instance + * of the given type is created and initialized with the arguments provided. + * + * @tparam Type Type of object to set. + * @tparam Args Types of arguments to use to construct the object. + * @param args Parameters to use to initialize the value. + * @return A reference to the newly created object. + */ + template + Type & set(Args &&... args) { + unset(); + vars.push_back(variable_data{type_info::id(), { new Type{std::forward(args)...}, [](void *instance) { delete static_cast(instance); } }}); + return *static_cast(vars.back().value.get()); + } + + /** + * @brief Unsets a context variable if it exists. + * @tparam Type Type of object to set. + */ + template + void unset() { + vars.erase(std::remove_if(vars.begin(), vars.end(), [](auto &&var) { + return var.type_id == type_info::id(); + }), vars.end()); + } + + /** + * @brief Binds an object to the context of the registry. + * + * In case the context doesn't contain the given object, the parameters + * provided are used to construct it. + * + * @tparam Type Type of object to set. + * @tparam Args Types of arguments to use to construct the object. + * @param args Parameters to use to initialize the object. + * @return A reference to the object in the context of the registry. + */ + template + Type & ctx_or_set(Args &&... args) { + auto *value = try_ctx(); + return value ? *value : set(std::forward(args)...); + } + + /** + * @brief Returns a pointer to an object in the context of the registry. + * @tparam Type Type of object to get. + * @return A pointer to the object if it exists in the context of the + * registry, a null pointer otherwise. + */ + template + const Type * try_ctx() const { + auto it = std::find_if(vars.cbegin(), vars.cend(), [](auto &&var) { return var.type_id == type_info::id(); }); + return it == vars.cend() ? nullptr : static_cast(it->value.get()); + } + + /*! @copydoc try_ctx */ + template + Type * try_ctx() { + return const_cast(std::as_const(*this).template try_ctx()); + } + + /** + * @brief Returns a reference to an object in the context of the registry. + * + * @warning + * Attempting to get a context variable that doesn't exist results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid requests. + * + * @tparam Type Type of object to get. + * @return A valid reference to the object in the context of the registry. + */ + template + const Type & ctx() const { + const auto *instance = try_ctx(); + ENTT_ASSERT(instance); + return *instance; + } + + /*! @copydoc ctx */ + template + Type & ctx() { + return const_cast(std::as_const(*this).template ctx()); + } + + /** + * @brief Visits a registry and returns the types for its context variables. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const id_type); + * @endcode + * + * Returned identifiers are those of the context variables currently set. + * + * @sa type_info + * + * @warning + * It's not specified whether a context variable created during the visit is + * returned or not to the caller. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void ctx(Func func) const { + for(auto pos = vars.size(); pos; --pos) { + func(vars[pos-1].type_id); + } + } + +private: + std::vector groups{}; + mutable std::vector pools{}; + std::vector entities{}; + std::vector vars{}; + entity_type destroyed{null}; +}; + + +} + + +#endif + +// #include "entity.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Dedicated to those who aren't confident with the + * entity-component-system architecture. + * + * Tiny wrapper around a registry, for all those users that aren't confident + * with entity-component-system architecture and prefer to iterate objects + * directly. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct basic_actor { + /*! @brief Type of registry used internally. */ + using registry_type = basic_registry; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + basic_actor() ENTT_NOEXCEPT + : entt{entt::null}, reg{nullptr} + {} + + /** + * @brief Move constructor. + * + * After actor move construction, instances that have been moved from are + * placed in a valid but unspecified state. It's highly discouraged to + * continue using them. + * + * @param other The instance to move from. + */ + basic_actor(basic_actor &&other) ENTT_NOEXCEPT + : entt{other.entt}, reg{other.reg} + { + other.entt = null; + } + + /** + * @brief Constructs an actor from a given registry. + * @param ref An instance of the registry class. + */ + explicit basic_actor(registry_type &ref) + : entt{ref.create()}, reg{&ref} + {} + + /** + * @brief Constructs an actor from a given entity. + * @param entity A valid entity identifier. + * @param ref An instance of the registry class. + */ + explicit basic_actor(entity_type entity, registry_type &ref) ENTT_NOEXCEPT + : entt{entity}, reg{&ref} + { + ENTT_ASSERT(ref.valid(entity)); + } + + /*! @brief Default destructor. */ + virtual ~basic_actor() { + if(*this) { + reg->destroy(entt); + } + } + + /** + * @brief Move assignment operator. + * + * After actor move assignment, instances that have been moved from are + * placed in a valid but unspecified state. It's highly discouraged to + * continue using them. + * + * @param other The instance to move from. + * @return This actor. + */ + basic_actor & operator=(basic_actor &&other) ENTT_NOEXCEPT { + if(this != &other) { + auto tmp{std::move(other)}; + std::swap(reg, tmp.reg); + std::swap(entt, tmp.entt); + } + + return *this; + } + + /** + * @brief Assigns the given component to an actor. + * + * A new instance of the given component is created and initialized with the + * arguments provided (the component must have a proper constructor or be of + * aggregate type). Then the component is assigned to the actor.
+ * In case the actor already has a component of the given type, it's + * replaced with the new one. + * + * @tparam Component Type of the component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) assign(Args &&... args) { + return reg->template emplace_or_replace(entt, std::forward(args)...); + } + + /** + * @brief Removes the given component from an actor. + * @tparam Component Type of the component to remove. + */ + template + void remove() { + reg->template remove(entt); + } + + /** + * @brief Checks if an actor has the given components. + * @tparam Component Components for which to perform the check. + * @return True if the actor has all the components, false otherwise. + */ + template + bool has() const { + return reg->template has(entt); + } + + /** + * @brief Returns references to the given components for an actor. + * @tparam Component Types of components to get. + * @return References to the components owned by the actor. + */ + template + decltype(auto) get() const { + return std::as_const(*reg).template get(entt); + } + + /*! @copydoc get */ + template + decltype(auto) get() { + return reg->template get(entt); + } + + /** + * @brief Returns pointers to the given components for an actor. + * @tparam Component Types of components to get. + * @return Pointers to the components owned by the actor. + */ + template + auto try_get() const { + return std::as_const(*reg).template try_get(entt); + } + + /*! @copydoc try_get */ + template + auto try_get() { + return reg->template try_get(entt); + } + + /** + * @brief Returns a reference to the underlying registry. + * @return A reference to the underlying registry. + */ + const registry_type & backend() const ENTT_NOEXCEPT { + return *reg; + } + + /*! @copydoc backend */ + registry_type & backend() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).backend()); + } + + /** + * @brief Returns the entity associated with an actor. + * @return The entity associated with the actor. + */ + entity_type entity() const ENTT_NOEXCEPT { + return entt; + } + + /** + * @brief Checks if an actor refers to a valid entity or not. + * @return True if the actor refers to a valid entity, false otherwise. + */ + explicit operator bool() const { + return reg && reg->valid(entt); + } + +private: + entity_type entt; + registry_type *reg; +}; + + +} + + +#endif + +// #include "entity/entity.hpp" + +// #include "entity/group.hpp" + +// #include "entity/helper.hpp" +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + + +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "../signal/delegate.hpp" + +// #include "registry.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Converts a registry to a view. + * @tparam Const Constness of the accepted registry. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct as_view { + /*! @brief Type of registry to convert. */ + using registry_type = std::conditional_t, entt::basic_registry>; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_view(registry_type &source) ENTT_NOEXCEPT: reg{source} {} + + /** + * @brief Conversion function from a registry to a view. + * @tparam Exclude Types of components used to filter the view. + * @tparam Component Type of components used to construct the view. + * @return A newly created view. + */ + template + operator entt::basic_view() const { + return reg.template view(Exclude{}); + } + +private: + registry_type ® +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the constness of a registry directly from the instance + * provided to the constructor. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_view(basic_registry &) ENTT_NOEXCEPT -> as_view; + + +/*! @copydoc as_view */ +template +as_view(const basic_registry &) ENTT_NOEXCEPT -> as_view; + + +/** + * @brief Converts a registry to a group. + * @tparam Const Constness of the accepted registry. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct as_group { + /*! @brief Type of registry to convert. */ + using registry_type = std::conditional_t, entt::basic_registry>; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {} + + /** + * @brief Conversion function from a registry to a group. + * @tparam Exclude Types of components used to filter the group. + * @tparam Get Types of components observed by the group. + * @tparam Owned Types of components owned by the group. + * @return A newly created group. + */ + template + operator entt::basic_group() const { + return reg.template group(Get{}, Exclude{}); + } + +private: + registry_type ® +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the constness of a registry directly from the instance + * provided to the constructor. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_group(basic_registry &) ENTT_NOEXCEPT -> as_group; + + +/*! @copydoc as_group */ +template +as_group(const basic_registry &) ENTT_NOEXCEPT -> as_group; + + + +/** + * @brief Helper to create a listener that directly invokes a member function. + * @tparam Member Member function to invoke on a component of the given type. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @param reg A registry that contains the given entity and its components. + * @param entt Entity from which to get the component. + */ +template +void invoke(basic_registry ®, const Entity entt) { + static_assert(std::is_member_function_pointer_v); + delegate &, const Entity)> func; + func.template connect(reg.template get>(entt)); + func(reg, entt); +} + + +} + + +#endif + +// #include "entity/observer.hpp" +#ifndef ENTT_ENTITY_OBSERVER_HPP +#define ENTT_ENTITY_OBSERVER_HPP + + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "registry.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/*! @brief Grouping matcher. */ +template +struct matcher {}; + + +/** + * @brief Collector. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +struct basic_collector; + + +/** + * @brief Collector. + * + * A collector contains a set of rules (literally, matchers) to use to track + * entities.
+ * Its main purpose is to generate a descriptor that allows an observer to know + * how to connect to a registry. + */ +template<> +struct basic_collector<> { + /** + * @brief Adds a grouping matcher to the collector. + * @tparam AllOf Types of components tracked by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto group(exclude_t = {}) ENTT_NOEXCEPT { + return basic_collector, type_list<>, type_list, AllOf...>>{}; + } + + /** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of component for which changes should be detected. + * @return The updated collector. + */ + template + static constexpr auto update() ENTT_NOEXCEPT { + return basic_collector, type_list<>, AnyOf>>{}; + } + + /*! @copydoc update */ + template + [[deprecated("use ::update instead")]] + static constexpr auto replace() ENTT_NOEXCEPT { + return update(); + } +}; + +/** + * @brief Collector. + * @copydetails basic_collector<> + * @tparam Reject Untracked types used to filter out entities. + * @tparam Require Untracked types required by the matcher. + * @tparam Rule Specific details of the current matcher. + * @tparam Other Other matchers. + */ +template +struct basic_collector, type_list, Rule...>, Other...> { + /*! @brief Current matcher. */ + using current_type = matcher, type_list, Rule...>; + + /** + * @brief Adds a grouping matcher to the collector. + * @tparam AllOf Types of components tracked by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto group(exclude_t = {}) ENTT_NOEXCEPT { + return basic_collector, type_list<>, type_list, AllOf...>, current_type, Other...>{}; + } + + /** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of component for which changes should be detected. + * @return The updated collector. + */ + template + static constexpr auto update() ENTT_NOEXCEPT { + return basic_collector, type_list<>, AnyOf>, current_type, Other...>{}; + } + + /*! @copydoc update */ + template + [[deprecated("use ::update instead")]] + static constexpr auto replace() ENTT_NOEXCEPT { + return update(); + } + + + /** + * @brief Updates the filter of the last added matcher. + * @tparam AllOf Types of components required by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto where(exclude_t = {}) ENTT_NOEXCEPT { + using extended_type = matcher, type_list, Rule...>; + return basic_collector{}; + } +}; + + +/*! @brief Variable template used to ease the definition of collectors. */ +inline constexpr basic_collector<> collector{}; + + +/** + * @brief Observer. + * + * An observer returns all the entities and only the entities that fit the + * requirements of at least one matcher. Moreover, it's guaranteed that the + * entity list is tightly packed in memory for fast iterations.
+ * In general, observers don't stay true to the order of any set of components. + * + * Observers work mainly with two types of matchers, provided through a + * collector: + * + * * Observing matcher: an observer will return at least all the living entities + * for which one or more of the given components have been updated and not yet + * destroyed. + * * Grouping matcher: an observer will return at least all the living entities + * that would have entered the given group if it existed and that would have + * not yet left it. + * + * If an entity respects the requirements of multiple matchers, it will be + * returned once and only once by the observer in any case. + * + * Matchers support also filtering by means of a _where_ clause that accepts + * both a list of types and an exclusion list.
+ * Whenever a matcher finds that an entity matches its requirements, the + * condition of the filter is verified before to register the entity itself. + * Moreover, a registered entity isn't returned by the observer if the condition + * set by the filter is broken in the meantime. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all the other cases, modifying the pools of the given components in any + * way invalidates all the iterators and using them results in undefined + * behavior. + * + * @warning + * Lifetime of an observer doesn't necessarily have to overcome that of the + * registry to which it is connected. However, the observer must be disconnected + * from the registry before being destroyed to avoid crashes due to dangling + * pointers. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_observer { + using payload_type = std::uint32_t; + + template + struct matcher_handler; + + template + struct matcher_handler, type_list, AnyOf>> { + template + static void maybe_valid_if(basic_observer &obs, const basic_registry ®, const Entity entt) { + if(reg.template has(entt) && !reg.template any(entt)) { + if(auto *comp = obs.view.try_get(entt); !comp) { + obs.view.emplace(entt); + } + + obs.view.get(entt) |= (1 << Index); + } + } + + template + static void discard_if(basic_observer &obs, const basic_registry &, const Entity entt) { + if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) { + obs.view.erase(entt); + } + } + + template + static void connect(basic_observer &obs, basic_registry ®) { + (reg.template on_destroy().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&discard_if>(obs), ...); + reg.template on_update().template connect<&maybe_valid_if>(obs); + reg.template on_destroy().template connect<&discard_if>(obs); + } + + static void disconnect(basic_observer &obs, basic_registry ®) { + (reg.template on_destroy().disconnect(obs), ...); + (reg.template on_construct().disconnect(obs), ...); + reg.template on_update().disconnect(obs); + reg.template on_destroy().disconnect(obs); + } + }; + + template + struct matcher_handler, type_list, type_list, AllOf...>> { + template + static void maybe_valid_if(basic_observer &obs, const basic_registry ®, const Entity entt) { + if(reg.template has(entt) && !reg.template any(entt)) { + if(auto *comp = obs.view.try_get(entt); !comp) { + obs.view.emplace(entt); + } + + obs.view.get(entt) |= (1 << Index); + } + } + + template + static void discard_if(basic_observer &obs, const basic_registry &, const Entity entt) { + if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) { + obs.view.erase(entt); + } + } + + template + static void connect(basic_observer &obs, basic_registry ®) { + (reg.template on_destroy().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&maybe_valid_if>(obs), ...); + (reg.template on_destroy().template connect<&maybe_valid_if>(obs), ...); + (reg.template on_destroy().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&discard_if>(obs), ...); + } + + static void disconnect(basic_observer &obs, basic_registry ®) { + (reg.template on_destroy().disconnect(obs), ...); + (reg.template on_construct().disconnect(obs), ...); + (reg.template on_construct().disconnect(obs), ...); + (reg.template on_destroy().disconnect(obs), ...); + (reg.template on_destroy().disconnect(obs), ...); + (reg.template on_construct().disconnect(obs), ...); + } + }; + + template + static void disconnect(basic_observer &obs, basic_registry ®) { + (matcher_handler::disconnect(obs, reg), ...); + } + + template + void connect(basic_registry ®, std::index_sequence) { + static_assert(sizeof...(Matcher) < std::numeric_limits::digits); + (matcher_handler::template connect(*this, reg), ...); + release = &basic_observer::disconnect; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Input iterator type. */ + using iterator = typename sparse_set::iterator; + + /*! @brief Default constructor. */ + basic_observer() + : target{}, release{}, view{} + {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_observer(const basic_observer &) = delete; + /*! @brief Default move constructor, deleted on purpose. */ + basic_observer(basic_observer &&) = delete; + + /** + * @brief Creates an observer and connects it to a given registry. + * @tparam Matcher Types of matchers to use to initialize the observer. + * @param reg A valid reference to a registry. + */ + template + basic_observer(basic_registry ®, basic_collector) + : target{®}, + release{}, + view{} + { + connect(reg, std::index_sequence_for{}); + } + + /*! @brief Default destructor. */ + ~basic_observer() = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This observer. + */ + basic_observer & operator=(const basic_observer &) = delete; + + /** + * @brief Default move assignment operator, deleted on purpose. + * @return This observer. + */ + basic_observer & operator=(basic_observer &&) = delete; + + /** + * @brief Connects an observer to a given registry. + * @tparam Matcher Types of matchers to use to initialize the observer. + * @param reg A valid reference to a registry. + */ + template + void connect(basic_registry ®, basic_collector) { + disconnect(); + connect(reg, std::index_sequence_for{}); + target = ® + view.clear(); + } + + /*! @brief Disconnects an observer from the registry it keeps track of. */ + void disconnect() { + if(release) { + release(*this, *target); + release = nullptr; + } + } + + /** + * @brief Returns the number of elements in an observer. + * @return Number of elements. + */ + size_type size() const ENTT_NOEXCEPT { + return view.size(); + } + + /** + * @brief Checks whether an observer is empty. + * @return True if the observer is empty, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return view.empty(); + } + + /** + * @brief Direct access to the list of entities of the observer. + * + * The returned pointer is such that range `[data(), data() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order of the entities. Use `begin` and + * `end` if you want to iterate the observer in the expected order. + * + * @return A pointer to the array of entities. + */ + const entity_type * data() const ENTT_NOEXCEPT { + return view.data(); + } + + /** + * @brief Returns an iterator to the first entity of the observer. + * + * The returned iterator points to the first entity of the observer. If the + * container is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the observer. + */ + iterator begin() const ENTT_NOEXCEPT { + return view.sparse_set::begin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the observer. + * + * The returned iterator points to the entity following the last entity of + * the observer. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * observer. + */ + iterator end() const ENTT_NOEXCEPT { + return view.sparse_set::end(); + } + + /*! @brief Clears the underlying container. */ + void clear() ENTT_NOEXCEPT { + view.clear(); + } + + /** + * @brief Iterates entities and applies the given function object to them. + * + * The function object is invoked for each entity.
+ * The signature of the function must be equivalent to the following form: + * + * @code{.cpp} + * void(const entity_type); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + static_assert(std::is_invocable_v); + + for(const auto entity: *this) { + func(entity); + } + } + + /** + * @brief Iterates entities and applies the given function object to them, + * then clears the observer. + * + * @sa each + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) { + std::as_const(*this).each(std::move(func)); + clear(); + } + +private: + basic_registry *target; + void(* release)(basic_observer &, basic_registry &); + storage view; +}; + + +} + + +#endif + +// #include "entity/registry.hpp" + +// #include "entity/runtime_view.hpp" + +// #include "entity/snapshot.hpp" + +// #include "entity/sparse_set.hpp" + +// #include "entity/storage.hpp" + +// #include "entity/utility.hpp" + +// #include "entity/view.hpp" + +// #include "locator/locator.hpp" +#ifndef ENTT_LOCATOR_LOCATOR_HPP +#define ENTT_LOCATOR_LOCATOR_HPP + + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty_v +#else +# include +# // sfinae-friendly definition +# define ENTT_IS_EMPTY(Type) (false && std::is_empty_v) +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) __VA_ARGS__ +# endif +#endif + + +#endif + + + +namespace entt { + + +/** + * @brief Service locator, nothing more. + * + * A service locator can be used to do what it promises: locate services.
+ * Usually service locators are tightly bound to the services they expose and + * thus it's hard to define a general purpose class to do that. This template + * based implementation tries to fill the gap and to get rid of the burden of + * defining a different specific locator for each application. + * + * @tparam Service Type of service managed by the locator. + */ +template +struct service_locator { + /*! @brief Type of service offered. */ + using service_type = Service; + + /*! @brief Default constructor, deleted on purpose. */ + service_locator() = delete; + /*! @brief Default destructor, deleted on purpose. */ + ~service_locator() = delete; + + /** + * @brief Tests if a valid service implementation is set. + * @return True if the service is set, false otherwise. + */ + static bool empty() ENTT_NOEXCEPT { + return !static_cast(service); + } + + /** + * @brief Returns a weak pointer to a service implementation, if any. + * + * Clients of a service shouldn't retain references to it. The recommended + * way is to retrieve the service implementation currently set each and + * every time the need of using it arises. Otherwise users can incur in + * unexpected behaviors. + * + * @return A reference to the service implementation currently set, if any. + */ + static std::weak_ptr get() ENTT_NOEXCEPT { + return service; + } + + /** + * @brief Returns a weak reference to a service implementation, if any. + * + * Clients of a service shouldn't retain references to it. The recommended + * way is to retrieve the service implementation currently set each and + * every time the need of using it arises. Otherwise users can incur in + * unexpected behaviors. + * + * @warning + * In case no service implementation has been set, a call to this function + * results in undefined behavior. + * + * @return A reference to the service implementation currently set, if any. + */ + static Service & ref() ENTT_NOEXCEPT { + return *service; + } + + /** + * @brief Sets or replaces a service. + * @tparam Impl Type of the new service to use. + * @tparam Args Types of arguments to use to construct the service. + * @param args Parameters to use to construct the service. + */ + template + static void set(Args &&... args) { + service = std::make_shared(std::forward(args)...); + } + + /** + * @brief Sets or replaces a service. + * @param ptr Service to use to replace the current one. + */ + static void set(std::shared_ptr ptr) { + ENTT_ASSERT(static_cast(ptr)); + service = std::move(ptr); + } + + /** + * @brief Resets a service. + * + * The service is no longer valid after a reset. + */ + static void reset() { + service.reset(); + } + +private: + inline static std::shared_ptr service = nullptr; +}; + + +} + + +#endif + +// #include "meta/factory.hpp" +#ifndef ENTT_META_FACTORY_HPP +#define ENTT_META_FACTORY_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty_v +#else +# include +# // sfinae-friendly definition +# define ENTT_IS_EMPTY(Type) (false && std::is_empty_v) +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) __VA_ARGS__ +# endif +#endif + + +#endif + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty_v +#else +# include +# // sfinae-friendly definition +# define ENTT_IS_EMPTY(Type) (false && std::is_empty_v) +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) __VA_ARGS__ +# endif +#endif + + +#endif + + + +namespace entt { + + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + + +} + + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + + +#endif + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct fnv1a_traits; + + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string { + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {} + const Char *str; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT { + auto value = traits_type::offset; + + while(*curr != 0) { + value = (value ^ static_cast(*(curr++))) * traits_type::prime; + } + + return value; + } + +public: + /*! @brief Character type. */ + using value_type = Char; + /*! @brief Unsigned integer type. */ + using hash_type = id_type; + + /** + * @brief Returns directly the numeric representation of a string. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * const auto value = basic_hashed_string::to_value("my.png"); + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + * @return The numeric representation of the string. + */ + template + static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); + } + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_NOEXCEPT + : str{nullptr}, hash{} + {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * basic_hashed_string hs{"my.png"}; + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param curr Human-readable identifer. + */ + template + constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT + : str{curr}, hash{helper(curr)} + {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + : str{wrapper.str}, hash{helper(wrapper.str)} + {} + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the instance. + */ + constexpr const value_type * data() const ENTT_NOEXCEPT { + return str; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + constexpr hash_type value() const ENTT_NOEXCEPT { + return hash; + } + + /*! @copydoc data */ + constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } + + /** + * @brief Compares two hashed strings. + * @param other Hashed string with which to compare. + * @return True if the two hashed strings are identical, false otherwise. + */ + constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + return hash == other.hash; + } + +private: + const value_type *str; + hash_type hash; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the character type of the hashed string directly from a + * human-readable identifer provided to the constructor. + * + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + */ +template +basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT +-> basic_hashed_string; + + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template +constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + + +} + + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_string{str}; +} + + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_wstring{str}; +} + + +#endif + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_index { + static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/** + * @brief Type index. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } +}; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * indexable, false otherwise. + * @tparam Type Potentially indexable type. + */ +template +struct has_type_index: std::false_type {}; + + +/*! @brief has_type_index */ +template +struct has_type_index::value())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially indexable type. + */ +template +inline constexpr bool has_type_index_v = has_type_index::value; + + +/** + * @brief Type info. + * @tparam Type Type for which to generate information. + */ +template +struct ENTT_API type_info { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + static ENTT_PRETTY_FUNCTION_CONSTEXPR() id_type id() ENTT_NOEXCEPT { + ENTT_PRETTY_FUNCTION_CONSTEXPR(static const) auto value = entt::hashed_string::value(ENTT_PRETTY_FUNCTION); + return value; + } +#else + static id_type id() ENTT_NOEXCEPT { + return type_index::value(); + } +#endif +}; + + +} + + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" + +// #include "hashed_string.hpp" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to ease the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond TURN_OFF_DOXYGEN */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + + +/*! @brief A class to use to push around lists of types, nothing more. */ +template +struct type_list {}; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_size; + + +/** + * @brief Compile-time number of elements in a type list. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_size> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + */ +template +inline constexpr auto type_list_size_v = type_list_size::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template> +struct is_equality_comparable: std::false_type {}; + + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + + +/** + * @brief Defines an enum class to use for opaque identifiers and a dedicate + * `to_integer` function to convert the identifiers to their underlying type. + * @param clazz The name to use for the enum class. + * @param type The underlying type for the enum class. + */ +#define ENTT_OPAQUE_TYPE(clazz, type)\ + enum class clazz: type {};\ + constexpr auto to_integral(const clazz id) ENTT_NOEXCEPT {\ + return static_cast>(id);\ + }\ + static_assert(true) + + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + + +#include +// #include "../config/config.h" + + + +namespace entt { + + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + constexpr Type && operator()(Type &&value) const ENTT_NOEXCEPT { + return std::forward(value); + } +}; + + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +constexpr auto overload(Type Class:: *member) ENTT_NOEXCEPT { return member; } + + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +constexpr auto overload(Func *func) ENTT_NOEXCEPT { return func; } + + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive): + func{std::move(recursive)} + {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&... args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&... args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + + +} + + +#endif + +// #include "meta.hpp" +#ifndef ENTT_META_META_HPP +#define ENTT_META_META_HPP + + +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + + + +namespace entt { + + +class meta_any; +class meta_type; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct meta_type_node; + + +struct meta_prop_node { + meta_prop_node * next; + meta_any(* const key)(); + meta_any(* const value)(); +}; + + +struct meta_base_node { + meta_type_node * const parent; + meta_base_node * next; + meta_type_node *(* const type)() ENTT_NOEXCEPT; + void *(* const cast)(void *) ENTT_NOEXCEPT; +}; + + +struct meta_conv_node { + meta_type_node * const parent; + meta_conv_node * next; + meta_type_node *(* const type)() ENTT_NOEXCEPT; + meta_any(* const conv)(const void *); +}; + + +struct meta_ctor_node { + using size_type = std::size_t; + meta_type_node * const parent; + meta_ctor_node * next; + meta_prop_node * prop; + const size_type size; + meta_type_node *(* const arg)(size_type) ENTT_NOEXCEPT; + meta_any(* const invoke)(meta_any * const); +}; + + +struct meta_dtor_node { + meta_type_node * const parent; + void(* const invoke)(void *); +}; + + +struct meta_data_node { + id_type id; + meta_type_node * const parent; + meta_data_node * next; + meta_prop_node * prop; + const bool is_const; + const bool is_static; + meta_type_node *(* const type)() ENTT_NOEXCEPT; + bool(* const set)(meta_any, meta_any, meta_any); + meta_any(* const get)(meta_any, meta_any); +}; + + +struct meta_func_node { + using size_type = std::size_t; + id_type id; + meta_type_node * const parent; + meta_func_node * next; + meta_prop_node * prop; + const size_type size; + const bool is_const; + const bool is_static; + meta_type_node *(* const ret)() ENTT_NOEXCEPT; + meta_type_node *(* const arg)(size_type) ENTT_NOEXCEPT; + meta_any(* const invoke)(meta_any, meta_any *); +}; + + +struct meta_type_node { + using size_type = std::size_t; + const id_type type_id; + id_type id; + meta_type_node * next; + meta_prop_node * prop; + const bool is_void; + const bool is_integral; + const bool is_floating_point; + const bool is_array; + const bool is_enum; + const bool is_union; + const bool is_class; + const bool is_pointer; + const bool is_function_pointer; + const bool is_member_object_pointer; + const bool is_member_function_pointer; + const size_type extent; + bool(* const compare)(const void *, const void *); + meta_type_node *(* const remove_pointer)() ENTT_NOEXCEPT; + meta_type_node *(* const remove_extent)() ENTT_NOEXCEPT; + meta_base_node *base{nullptr}; + meta_conv_node *conv{nullptr}; + meta_ctor_node *ctor{nullptr}; + meta_dtor_node *dtor{nullptr}; + meta_data_node *data{nullptr}; + meta_func_node *func{nullptr}; +}; + + +template +void visit(Op &op, Node *node) { + while(node) { + op(Type{node}); + node = node->next; + } +} + + +template +void visit(Op &op, const internal::meta_type_node *node) { + if(node) { + internal::visit(op, node->*Member); + auto *next = node->base; + + while(next) { + visit(op, next->type()); + next = next->next; + } + } +} + + +template +auto find_if(const Op &op, Node *node) { + while(node && !op(node)) { + node = node->next; + } + + return node; +} + + +template +auto find_if(const Op &op, const meta_type_node *node) +-> decltype(find_if(op, node->*Member)) { + decltype(find_if(op, node->*Member)) ret = nullptr; + + if(node) { + ret = find_if(op, node->*Member); + auto *next = node->base; + + while(next && !ret) { + ret = find_if(op, next->type()); + next = next->next; + } + } + + return ret; +} + + +template +bool compare(const void *lhs, const void *rhs) { + if constexpr(!std::is_function_v && is_equality_comparable_v) { + return *static_cast(lhs) == *static_cast(rhs); + } else { + return lhs == rhs; + } +} + + +struct ENTT_API meta_context { + inline static meta_type_node *local = nullptr; + inline static meta_type_node **global = &local; + + static void detach(const meta_type_node *node) ENTT_NOEXCEPT { + auto **it = global; + + while(*it && *it != node) { + it = &(*it)->next; + } + + if(*it) { + *it = (*it)->next; + } + } +}; + + +template +struct ENTT_API meta_node { + static_assert(std::is_same_v>>); + + static meta_type_node * resolve() ENTT_NOEXCEPT { + static meta_type_node node{ + type_info::id(), + {}, + nullptr, + nullptr, + std::is_void_v, + std::is_integral_v, + std::is_floating_point_v, + std::is_array_v, + std::is_enum_v, + std::is_union_v, + std::is_class_v, + std::is_pointer_v, + std::is_pointer_v && std::is_function_v>, + std::is_member_object_pointer_v, + std::is_member_function_pointer_v, + std::extent_v, + &compare, // workaround for an issue with VS2017 + &meta_node>>::resolve, + &meta_node>>::resolve + }; + + return &node; + } +}; + + +template +struct meta_info: meta_node>...> {}; + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/*! @brief Opaque container for a meta context. */ +struct meta_ctx { + /** + * @brief Binds the meta system to the given context. + * @param other A valid context to which to bind. + */ + static void bind(meta_ctx other) ENTT_NOEXCEPT { + internal::meta_context::global = other.ctx; + } + +private: + internal::meta_type_node **ctx{&internal::meta_context::local}; +}; + + +/** + * @brief Opaque container for values of any type. + * + * This class uses a technique called small buffer optimization (SBO) to get rid + * of memory allocations if possible. This should improve overall performance. + */ +class meta_any { + using storage_type = std::aligned_storage_t; + using copy_fn_type = void(meta_any &, const meta_any &); + using steal_fn_type = void(meta_any &, meta_any &); + using destroy_fn_type = void(meta_any &); + + template> + struct type_traits { + template + static void instance(meta_any &any, Args &&... args) { + any.instance = new Type{std::forward(args)...}; + new (&any.storage) Type *{static_cast(any.instance)}; + } + + static void destroy(meta_any &any) { + const auto * const node = internal::meta_info::resolve(); + if(node->dtor) { node->dtor->invoke(any.instance); } + delete static_cast(any.instance); + } + + static void copy(meta_any &to, const meta_any &from) { + auto *instance = new Type{*static_cast(from.instance)}; + new (&to.storage) Type *{instance}; + to.instance = instance; + } + + static void steal(meta_any &to, meta_any &from) { + new (&to.storage) Type *{static_cast(from.instance)}; + to.instance = from.instance; + } + }; + + template + struct type_traits>> { + template + static void instance(meta_any &any, Args &&... args) { + any.instance = new (&any.storage) Type{std::forward(args)...}; + } + + static void destroy(meta_any &any) { + const auto * const node = internal::meta_info::resolve(); + if(node->dtor) { node->dtor->invoke(any.instance); } + static_cast(any.instance)->~Type(); + } + + static void copy(meta_any &to, const meta_any &from) { + to.instance = new (&to.storage) Type{*static_cast(from.instance)}; + } + + static void steal(meta_any &to, meta_any &from) { + to.instance = new (&to.storage) Type{std::move(*static_cast(from.instance))}; + destroy(from); + } + }; + + meta_any(const internal::meta_type_node *curr, void *ref) ENTT_NOEXCEPT + : meta_any{} + { + node = curr; + instance = ref; + } + +public: + /*! @brief Default constructor. */ + meta_any() ENTT_NOEXCEPT + : storage{}, + instance{}, + node{}, + destroy_fn{}, + copy_fn{}, + steal_fn{} + {} + + /** + * @brief Constructs a meta any by directly initializing the new object. + * @tparam Type Type of object to use to initialize the container. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(std::in_place_type_t, [[maybe_unused]] Args &&... args) + : meta_any{} + { + node = internal::meta_info::resolve(); + + if constexpr(!std::is_void_v) { + using traits_type = type_traits>>; + traits_type::instance(*this, std::forward(args)...); + destroy_fn = &traits_type::destroy; + copy_fn = &traits_type::copy; + steal_fn = &traits_type::steal; + } + } + + /** + * @brief Constructs a meta any that holds an unmanaged object. + * @tparam Type Type of object to use to initialize the container. + * @param value An instance of an object to use to initialize the container. + */ + template + meta_any(std::reference_wrapper value) + : meta_any{internal::meta_info::resolve(), &value.get()} + {} + + /** + * @brief Constructs a meta any from a given value. + * @tparam Type Type of object to use to initialize the container. + * @param value An instance of an object to use to initialize the container. + */ + template>, meta_any>>> + meta_any(Type &&value) + : meta_any{std::in_place_type>>, std::forward(value)} + {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + meta_any(const meta_any &other) + : meta_any{} + { + node = other.node; + (other.copy_fn ? other.copy_fn : [](meta_any &to, const meta_any &from) { to.instance = from.instance; })(*this, other); + destroy_fn = other.destroy_fn; + copy_fn = other.copy_fn; + steal_fn = other.steal_fn; + } + + /** + * @brief Move constructor. + * + * After move construction, instances that have been moved from are placed + * in a valid but unspecified state. + * + * @param other The instance to move from. + */ + meta_any(meta_any &&other) + : meta_any{} + { + swap(*this, other); + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~meta_any() { + if(destroy_fn) { + destroy_fn(*this); + } + } + + /** + * @brief Assignment operator. + * @tparam Type Type of object to use to initialize the container. + * @param value An instance of an object to use to initialize the container. + * @return This meta any object. + */ + template + meta_any & operator=(Type &&value) { + return (*this = meta_any{std::forward(value)}); + } + + /** + * @brief Assignment operator. + * @param other The instance to assign from. + * @return This meta any object. + */ + meta_any & operator=(meta_any other) { + swap(other, *this); + return *this; + } + + /** + * @brief Returns the meta type of the underlying object. + * @return The meta type of the underlying object, if any. + */ + inline meta_type type() const ENTT_NOEXCEPT; + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + const void * data() const ENTT_NOEXCEPT { + return instance; + } + + /*! @copydoc data */ + void * data() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).data()); + } + + /** + * @brief Tries to cast an instance to a given type. + * @tparam Type Type to which to cast the instance. + * @return A (possibly null) pointer to the contained instance. + */ + template + const Type * try_cast() const { + void *ret = nullptr; + + if(const auto type_id = internal::meta_info::resolve()->type_id; node && node->type_id == type_id) { + ret = instance; + } else if(const auto *base = internal::find_if<&internal::meta_type_node::base>([type_id](const auto *curr) { return curr->type()->type_id == type_id; }, node); base) { + ret = base->cast(instance); + } + + return static_cast(ret); + } + + /*! @copydoc try_cast */ + template + Type * try_cast() { + return const_cast(std::as_const(*this).try_cast()); + } + + /** + * @brief Tries to cast an instance to a given type. + * + * The type of the instance must be such that the cast is possible. + * + * @warning + * Attempting to perform a cast that isn't viable results in undefined + * behavior.
+ * An assertion will abort the execution at runtime in debug mode in case + * the cast is not feasible. + * + * @tparam Type Type to which to cast the instance. + * @return A reference to the contained instance. + */ + template + const Type & cast() const { + auto * const actual = try_cast(); + ENTT_ASSERT(actual); + return *actual; + } + + /*! @copydoc cast */ + template + Type & cast() { + return const_cast(std::as_const(*this).cast()); + } + + /** + * @brief Tries to convert an instance to a given type and returns it. + * @tparam Type Type to which to convert the instance. + * @return A valid meta any object if the conversion is possible, an invalid + * one otherwise. + */ + template + meta_any convert() const { + meta_any any{}; + + if(const auto type_id = internal::meta_info::resolve()->type_id; node && node->type_id == type_id) { + any = *this; + } else if(const auto * const conv = internal::find_if<&internal::meta_type_node::conv>([type_id](const auto *curr) { return curr->type()->type_id == type_id; }, node); conv) { + any = conv->conv(instance); + } + + return any; + } + + /** + * @brief Tries to convert an instance to a given type. + * @tparam Type Type to which to convert the instance. + * @return True if the conversion is possible, false otherwise. + */ + template + bool convert() { + bool valid = (node && node->type_id == internal::meta_info::resolve()->type_id); + + if(!valid) { + if(auto any = std::as_const(*this).convert(); any) { + swap(any, *this); + valid = true; + } + } + + return valid; + } + + /** + * @brief Replaces the contained object by initializing a new instance + * directly. + * @tparam Type Type of object to use to initialize the container. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&... args) { + *this = meta_any{std::in_place_type_t{}, std::forward(args)...}; + } + + /** + * @brief Aliasing constructor. + * @return A meta any that shares a reference to an unmanaged object. + */ + meta_any ref() const ENTT_NOEXCEPT { + return meta_any{node, instance}; + } + + /** + * @brief Indirection operator for aliasing construction. + * @return A meta any that shares a reference to an unmanaged object. + */ + meta_any operator *() const ENTT_NOEXCEPT { + return ref(); + } + + /** + * @brief Returns false if a container is empty, true otherwise. + * @return False if the container is empty, true otherwise. + */ + explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + + /** + * @brief Checks if two containers differ in their content. + * @param other Container with which to compare. + * @return False if the two containers differ in their content, true + * otherwise. + */ + bool operator==(const meta_any &other) const { + return (!node && !other.node) || (node && other.node && node->type_id == other.node->type_id && node->compare(instance, other.instance)); + } + + /** + * @brief Swaps two meta any objects. + * @param lhs A valid meta any object. + * @param rhs A valid meta any object. + */ + friend void swap(meta_any &lhs, meta_any &rhs) { + if(lhs.steal_fn && rhs.steal_fn) { + meta_any buffer{}; + lhs.steal_fn(buffer, lhs); + rhs.steal_fn(lhs, rhs); + lhs.steal_fn(rhs, buffer); + } else if(lhs.steal_fn) { + lhs.steal_fn(rhs, lhs); + } else if(rhs.steal_fn) { + rhs.steal_fn(lhs, rhs); + } else { + std::swap(lhs.instance, rhs.instance); + } + + std::swap(lhs.node, rhs.node); + std::swap(lhs.destroy_fn, rhs.destroy_fn); + std::swap(lhs.copy_fn, rhs.copy_fn); + std::swap(lhs.steal_fn, rhs.steal_fn); + } + +private: + storage_type storage; + void *instance; + const internal::meta_type_node *node; + destroy_fn_type *destroy_fn; + copy_fn_type *copy_fn; + steal_fn_type *steal_fn; +}; + + +/** + * @brief Opaque pointers to instances of any type. + * + * A handle doesn't perform copies and isn't responsible for the contained + * object. It doesn't prolong the lifetime of the pointed instance.
+ * Handles are used to generate meta references to actual objects when needed. + */ +struct meta_handle { + /*! @brief Default constructor. */ + meta_handle() + : any{} + {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the container. + * @param value An instance of an object to use to initialize the container. + */ + template + meta_handle(Type &&value) ENTT_NOEXCEPT + : meta_handle{} + { + if constexpr(std::is_same_v>, meta_any>) { + any = *value; + } else { + static_assert(std::is_lvalue_reference_v); + any = std::ref(value); + } + } + + /*! @copydoc meta_any::operator* */ + meta_any operator *() const { + return any; + } + +private: + meta_any any; +}; + + +/** + * @brief Checks if two containers differ in their content. + * @param lhs A meta any object, either empty or not. + * @param rhs A meta any object, either empty or not. + * @return True if the two containers differ in their content, false otherwise. + */ +inline bool operator!=(const meta_any &lhs, const meta_any &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Opaque container for meta properties of any type. */ +struct meta_prop { + /** + * @brief Constructs an instance from a given node. + * @param curr The underlying node with which to construct the instance. + */ + meta_prop(const internal::meta_prop_node *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /** + * @brief Returns the stored key. + * @return A meta any containing the key stored with the given property. + */ + meta_any key() const { + return node->key(); + } + + /** + * @brief Returns the stored value. + * @return A meta any containing the value stored with the given property. + */ + meta_any value() const { + return node->value(); + } + + /** + * @brief Returns true if a meta object is valid, false otherwise. + * @return True if the meta object is valid, false otherwise. + */ + explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const internal::meta_prop_node *node; +}; + + +/*! @brief Opaque container for meta base classes. */ +struct meta_base { + /*! @copydoc meta_prop::meta_prop */ + meta_base(const internal::meta_base_node *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /** + * @brief Returns the meta type to which a meta object belongs. + * @return The meta type to which the meta object belongs. + */ + inline meta_type parent() const ENTT_NOEXCEPT; + + /*! @copydoc meta_any::type */ + inline meta_type type() const ENTT_NOEXCEPT; + + /** + * @brief Casts an instance from a parent type to a base type. + * @param instance The instance to cast. + * @return An opaque pointer to the base type. + */ + void * cast(void *instance) const ENTT_NOEXCEPT { + return node->cast(instance); + } + + /** + * @brief Returns true if a meta object is valid, false otherwise. + * @return True if the meta object is valid, false otherwise. + */ + explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const internal::meta_base_node *node; +}; + + +/*! @brief Opaque container for meta conversion functions. */ +struct meta_conv { + /*! @copydoc meta_prop::meta_prop */ + meta_conv(const internal::meta_conv_node *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /*! @copydoc meta_base::parent */ + inline meta_type parent() const ENTT_NOEXCEPT; + + /*! @copydoc meta_any::type */ + inline meta_type type() const ENTT_NOEXCEPT; + + /** + * @brief Converts an instance to a given type. + * @param instance The instance to convert. + * @return An opaque pointer to the instance to convert. + */ + meta_any convert(const void *instance) const { + return node->conv(instance); + } + + /** + * @brief Returns true if a meta object is valid, false otherwise. + * @return True if the meta object is valid, false otherwise. + */ + explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const internal::meta_conv_node *node; +}; + + +/*! @brief Opaque container for meta constructors. */ +struct meta_ctor { + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_ctor_node::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_ctor(const internal::meta_ctor_node *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /*! @copydoc meta_base::parent */ + inline meta_type parent() const ENTT_NOEXCEPT; + + /** + * @brief Returns the number of arguments accepted by a meta constructor. + * @return The number of arguments accepted by the meta constructor. + */ + size_type size() const ENTT_NOEXCEPT { + return node->size; + } + + /** + * @brief Returns the meta type of the i-th argument of a meta constructor. + * @param index The index of the argument of which to return the meta type. + * @return The meta type of the i-th argument of a meta constructor, if any. + */ + meta_type arg(size_type index) const ENTT_NOEXCEPT; + + /** + * @brief Creates an instance of the underlying type, if possible. + * + * To create a valid instance, the parameters must be such that a cast or + * conversion to the required types is possible. Otherwise, an empty and + * thus invalid container is returned. + * + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ + template + meta_any invoke([[maybe_unused]] Args &&... args) const { + if constexpr(sizeof...(Args) == 0) { + return sizeof...(Args) == size() ? node->invoke(nullptr) : meta_any{}; + } else { + meta_any arguments[]{std::forward(args)...}; + return sizeof...(Args) == size() ? node->invoke(arguments) : meta_any{}; + } + } + + /** + * @brief Iterates all the properties assigned to a meta constructor. + * @tparam Op Type of the function object to invoke. + * @param op A valid function object. + */ + template + std::enable_if_t, void> + prop(Op op) const { + internal::visit(op, node->prop); + } + + /** + * @brief Returns the property associated with a given key. + * @param key The key to use to search for a property. + * @return The property associated with the given key, if any. + */ + meta_prop prop(meta_any key) const { + return internal::find_if([key = std::move(key)](const auto *curr) { + return curr->key() == key; + }, node->prop); + } + + /** + * @brief Returns true if a meta object is valid, false otherwise. + * @return True if the meta object is valid, false otherwise. + */ + explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const internal::meta_ctor_node *node; +}; + + +/*! @brief Opaque container for meta data. */ +struct meta_data { + /*! @copydoc meta_prop::meta_prop */ + meta_data(const internal::meta_data_node *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /*! @copydoc meta_type::id */ + id_type id() const ENTT_NOEXCEPT { + return node->id; + } + + /*! @copydoc id */ + [[deprecated("use ::id instead")]] + id_type alias() const ENTT_NOEXCEPT { + return id(); + } + + /*! @copydoc meta_base::parent */ + inline meta_type parent() const ENTT_NOEXCEPT; + + /** + * @brief Indicates whether a given meta data is constant or not. + * @return True if the meta data is constant, false otherwise. + */ + bool is_const() const ENTT_NOEXCEPT { + return node->is_const; + } + + /** + * @brief Indicates whether a given meta data is static or not. + * @return True if the meta data is static, false otherwise. + */ + bool is_static() const ENTT_NOEXCEPT { + return node->is_static; + } + + /*! @copydoc meta_any::type */ + inline meta_type type() const ENTT_NOEXCEPT; + + /** + * @brief Sets the value of the variable enclosed by a given meta type. + * + * It must be possible to cast the instance to the parent type of the meta + * data. Otherwise, invoking the setter results in an undefined + * behavior.
+ * The type of the value must be such that a cast or conversion to the type + * of the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(meta_handle instance, Type &&value) const { + return node->set(*instance, {}, std::forward(value)); + } + + /** + * @brief Sets the i-th element of an array enclosed by a given meta type. + * + * It must be possible to cast the instance to the parent type of the meta + * data. Otherwise, invoking the setter results in an undefined + * behavior.
+ * The type of the value must be such that a cast or conversion to the array + * type is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param instance An opaque instance of the underlying type. + * @param index Position of the underlying element to set. + * @param value Parameter to use to set the underlying element. + * @return True in case of success, false otherwise. + */ + template + bool set(meta_handle instance, std::size_t index, Type &&value) const { + ENTT_ASSERT(index < node->type()->extent); + return node->set(*instance, index, std::forward(value)); + } + + /** + * @brief Gets the value of the variable enclosed by a given meta type. + * + * It must be possible to cast the instance to the parent type of the meta + * data. Otherwise, invoking the getter results in an undefined behavior. + * + * @param instance An opaque instance of the underlying type. + * @return A meta any containing the value of the underlying variable. + */ + meta_any get(meta_handle instance) const { + return node->get(*instance, {}); + } + + /** + * @brief Gets the i-th element of an array enclosed by a given meta type. + * + * It must be possible to cast the instance to the parent type of the meta + * data. Otherwise, invoking the getter results in an undefined behavior. + * + * @param instance An opaque instance of the underlying type. + * @param index Position of the underlying element to get. + * @return A meta any containing the value of the underlying element. + */ + meta_any get(meta_handle instance, std::size_t index) const { + ENTT_ASSERT(index < node->type()->extent); + return node->get(*instance, index); + } + + /** + * @brief Iterates all the properties assigned to a meta data. + * @tparam Op Type of the function object to invoke. + * @param op A valid function object. + */ + template + std::enable_if_t, void> + prop(Op op) const { + internal::visit(op, node->prop); + } + + /** + * @brief Returns the property associated with a given key. + * @param key The key to use to search for a property. + * @return The property associated with the given key, if any. + */ + meta_prop prop(meta_any key) const { + return internal::find_if([key = std::move(key)](const auto *curr) { + return curr->key() == key; + }, node->prop); + } + + /** + * @brief Returns true if a meta object is valid, false otherwise. + * @return True if the meta object is valid, false otherwise. + */ + explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const internal::meta_data_node *node; +}; + + +/*! @brief Opaque container for meta functions. */ +struct meta_func { + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_func_node::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_func(const internal::meta_func_node *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /*! @copydoc meta_type::id */ + id_type id() const ENTT_NOEXCEPT { + return node->id; + } + + /*! @copydoc id */ + [[deprecated("use ::id instead")]] + id_type alias() const ENTT_NOEXCEPT { + return id(); + } + + /*! @copydoc meta_base::parent */ + inline meta_type parent() const ENTT_NOEXCEPT; + + /** + * @brief Returns the number of arguments accepted by a meta function. + * @return The number of arguments accepted by the meta function. + */ + size_type size() const ENTT_NOEXCEPT { + return node->size; + } + + /** + * @brief Indicates whether a given meta function is constant or not. + * @return True if the meta function is constant, false otherwise. + */ + bool is_const() const ENTT_NOEXCEPT { + return node->is_const; + } + + /** + * @brief Indicates whether a given meta function is static or not. + * @return True if the meta function is static, false otherwise. + */ + bool is_static() const ENTT_NOEXCEPT { + return node->is_static; + } + + /** + * @brief Returns the meta type of the return type of a meta function. + * @return The meta type of the return type of the meta function. + */ + inline meta_type ret() const ENTT_NOEXCEPT; + + /** + * @brief Returns the meta type of the i-th argument of a meta function. + * @param index The index of the argument of which to return the meta type. + * @return The meta type of the i-th argument of a meta function, if any. + */ + inline meta_type arg(size_type index) const ENTT_NOEXCEPT; + + /** + * @brief Invokes the underlying function, if possible. + * + * To invoke a meta function, the parameters must be such that a cast or + * conversion to the required types is possible. Otherwise, an empty and + * thus invalid container is returned.
+ * It must be possible to cast the instance to the parent type of the meta + * function. Otherwise, invoking the underlying function results in an + * undefined behavior. + * + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ + template + meta_any invoke(meta_handle instance, Args &&... args) const { + meta_any arguments[]{*instance, std::forward(args)...}; + return sizeof...(Args) == size() ? node->invoke(arguments[0], &arguments[sizeof...(Args) != 0]) : meta_any{}; + } + + /** + * @brief Iterates all the properties assigned to a meta function. + * @tparam Op Type of the function object to invoke. + * @param op A valid function object. + */ + template + std::enable_if_t, void> + prop(Op op) const { + internal::visit(op, node->prop); + } + + /** + * @brief Returns the property associated with a given key. + * @param key The key to use to search for a property. + * @return The property associated with the given key, if any. + */ + meta_prop prop(meta_any key) const { + return internal::find_if([key = std::move(key)](const auto *curr) { + return curr->key() == key; + }, node->prop); + } + + /** + * @brief Returns true if a meta object is valid, false otherwise. + * @return True if the meta object is valid, false otherwise. + */ + explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const internal::meta_func_node *node; +}; + + +/*! @brief Opaque container for meta types. */ +class meta_type { + template + auto ctor(std::index_sequence) const { + return internal::find_if([](const auto *candidate) { + return candidate->size == sizeof...(Args) && ([](auto *from, auto *to) { + return (from->type_id == to->type_id) + || internal::find_if<&internal::meta_type_node::base>([to](const auto *curr) { return curr->type()->type_id == to->type_id; }, from) + || internal::find_if<&internal::meta_type_node::conv>([to](const auto *curr) { return curr->type()->type_id == to->type_id; }, from); + }(internal::meta_info::resolve(), candidate->arg(Indexes)) && ...); + }, node->ctor); + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_type_node::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_type(const internal::meta_type_node *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /** + * @brief Returns the type id of the underlying type. + * @return The type id of the underlying type. + */ + id_type type_id() const ENTT_NOEXCEPT { + return node->type_id; + } + + /** + * @brief Returns the identifier assigned to a given meta object. + * @return The identifier assigned to the meta object. + */ + id_type id() const ENTT_NOEXCEPT { + return node->id; + } + + /*! @copydoc id */ + [[deprecated("use ::id instead")]] + id_type alias() const ENTT_NOEXCEPT { + return id(); + } + + /** + * @brief Indicates whether a given meta type refers to void or not. + * @return True if the underlying type is void, false otherwise. + */ + bool is_void() const ENTT_NOEXCEPT { + return node->is_void; + } + + /** + * @brief Indicates whether a given meta type refers to an integral type or + * not. + * @return True if the underlying type is an integral type, false otherwise. + */ + bool is_integral() const ENTT_NOEXCEPT { + return node->is_integral; + } + + /** + * @brief Indicates whether a given meta type refers to a floating-point + * type or not. + * @return True if the underlying type is a floating-point type, false + * otherwise. + */ + bool is_floating_point() const ENTT_NOEXCEPT { + return node->is_floating_point; + } + + /** + * @brief Indicates whether a given meta type refers to an array type or + * not. + * @return True if the underlying type is an array type, false otherwise. + */ + bool is_array() const ENTT_NOEXCEPT { + return node->is_array; + } + + /** + * @brief Indicates whether a given meta type refers to an enum or not. + * @return True if the underlying type is an enum, false otherwise. + */ + bool is_enum() const ENTT_NOEXCEPT { + return node->is_enum; + } + + /** + * @brief Indicates whether a given meta type refers to an union or not. + * @return True if the underlying type is an union, false otherwise. + */ + bool is_union() const ENTT_NOEXCEPT { + return node->is_union; + } + + /** + * @brief Indicates whether a given meta type refers to a class or not. + * @return True if the underlying type is a class, false otherwise. + */ + bool is_class() const ENTT_NOEXCEPT { + return node->is_class; + } + + /** + * @brief Indicates whether a given meta type refers to a pointer or not. + * @return True if the underlying type is a pointer, false otherwise. + */ + bool is_pointer() const ENTT_NOEXCEPT { + return node->is_pointer; + } + + /** + * @brief Indicates whether a given meta type refers to a function pointer + * or not. + * @return True if the underlying type is a function pointer, false + * otherwise. + */ + bool is_function_pointer() const ENTT_NOEXCEPT { + return node->is_function_pointer; + } + + /** + * @brief Indicates whether a given meta type refers to a pointer to data + * member or not. + * @return True if the underlying type is a pointer to data member, false + * otherwise. + */ + bool is_member_object_pointer() const ENTT_NOEXCEPT { + return node->is_member_object_pointer; + } + + /** + * @brief Indicates whether a given meta type refers to a pointer to member + * function or not. + * @return True if the underlying type is a pointer to member function, + * false otherwise. + */ + bool is_member_function_pointer() const ENTT_NOEXCEPT { + return node->is_member_function_pointer; + } + + /** + * @brief If a given meta type refers to an array type, provides the number + * of elements of the array. + * @return The number of elements of the array if the underlying type is an + * array type, 0 otherwise. + */ + size_type extent() const ENTT_NOEXCEPT { + return node->extent; + } + + /** + * @brief Provides the meta type for which the pointer is defined. + * @return The meta type for which the pointer is defined or this meta type + * if it doesn't refer to a pointer type. + */ + meta_type remove_pointer() const ENTT_NOEXCEPT { + return node->remove_pointer(); + } + + /** + * @brief Provides the meta type for which the array is defined. + * @return The meta type for which the array is defined or this meta type + * if it doesn't refer to an array type. + */ + meta_type remove_extent() const ENTT_NOEXCEPT { + return node->remove_extent(); + } + + /** + * @brief Iterates all the meta bases of a meta type. + * @tparam Op Type of the function object to invoke. + * @param op A valid function object. + */ + template + std::enable_if_t, void> + base(Op op) const { + internal::visit<&internal::meta_type_node::base, meta_base>(op, node); + } + + /** + * @brief Returns the meta base associated with a given identifier. + * @param id Unique identifier. + * @return The meta base associated with the given identifier, if any. + */ + meta_base base(const id_type id) const { + return internal::find_if<&internal::meta_type_node::base>([id](const auto *curr) { + return curr->type()->id == id; + }, node); + } + + /** + * @brief Iterates all the meta conversion functions of a meta type. + * @tparam Op Type of the function object to invoke. + * @param op A valid function object. + */ + template + void conv(Op op) const { + internal::visit<&internal::meta_type_node::conv, meta_conv>(op, node); + } + + /** + * @brief Returns the meta conversion function associated with a given type. + * @tparam Type The type to use to search for a meta conversion function. + * @return The meta conversion function associated with the given type, if + * any. + */ + template + meta_conv conv() const { + return internal::find_if<&internal::meta_type_node::conv>([type_id = internal::meta_info::resolve()->type_id](const auto *curr) { + return curr->type()->type_id == type_id; + }, node); + } + + /** + * @brief Iterates all the meta constructors of a meta type. + * @tparam Op Type of the function object to invoke. + * @param op A valid function object. + */ + template + void ctor(Op op) const { + internal::visit(op, node->ctor); + } + + /** + * @brief Returns the meta constructor that accepts a given list of types of + * arguments. + * @return The requested meta constructor, if any. + */ + template + meta_ctor ctor() const { + return ctor(std::index_sequence_for{}); + } + + /** + * @brief Iterates all the meta data of a meta type. + * + * The meta data of the base classes will also be returned, if any. + * + * @tparam Op Type of the function object to invoke. + * @param op A valid function object. + */ + template + std::enable_if_t, void> + data(Op op) const { + internal::visit<&internal::meta_type_node::data, meta_data>(op, node); + } + + /** + * @brief Returns the meta data associated with a given identifier. + * + * The meta data of the base classes will also be visited, if any. + * + * @param id Unique identifier. + * @return The meta data associated with the given identifier, if any. + */ + meta_data data(const id_type id) const { + return internal::find_if<&internal::meta_type_node::data>([id](const auto *curr) { + return curr->id == id; + }, node); + } + + /** + * @brief Iterates all the meta functions of a meta type. + * + * The meta functions of the base classes will also be returned, if any. + * + * @tparam Op Type of the function object to invoke. + * @param op A valid function object. + */ + template + std::enable_if_t, void> + func(Op op) const { + internal::visit<&internal::meta_type_node::func, meta_func>(op, node); + } + + /** + * @brief Returns the meta function associated with a given identifier. + * + * The meta functions of the base classes will also be visited, if any. + * + * @param id Unique identifier. + * @return The meta function associated with the given identifier, if any. + */ + meta_func func(const id_type id) const { + return internal::find_if<&internal::meta_type_node::func>([id](const auto *curr) { + return curr->id == id; + }, node); + } + + /** + * @brief Creates an instance of the underlying type, if possible. + * + * To create a valid instance, the parameters must be such that a cast or + * conversion to the required types is possible. Otherwise, an empty and + * thus invalid container is returned. + * + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ + template + meta_any construct(Args &&... args) const { + auto construct_if = [this](meta_any *params) { + meta_any any{}; + + internal::find_if<&internal::meta_type_node::ctor>([params, &any](const auto *curr) { + return (curr->size == sizeof...(args)) && (any = curr->invoke(params)); + }, node); + + return any; + }; + + if constexpr(sizeof...(Args) == 0) { + return construct_if(nullptr); + } else { + meta_any arguments[]{std::forward(args)...}; + return construct_if(arguments); + } + } + + /** + * @brief Iterates all the properties assigned to a meta type. + * + * The properties of the base classes will also be returned, if any. + * + * @tparam Op Type of the function object to invoke. + * @param op A valid function object. + */ + template + std::enable_if_t, void> + prop(Op op) const { + internal::visit<&internal::meta_type_node::prop, meta_prop>(op, node); + } + + /** + * @brief Returns the property associated with a given key. + * + * The properties of the base classes will also be visited, if any. + * + * @param key The key to use to search for a property. + * @return The property associated with the given key, if any. + */ + meta_prop prop(meta_any key) const { + return internal::find_if<&internal::meta_type_node::prop>([key = std::move(key)](const auto *curr) { + return curr->key() == key; + }, node); + } + + /** + * @brief Returns true if a meta object is valid, false otherwise. + * @return True if the meta object is valid, false otherwise. + */ + explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + + /** + * @brief Checks if two meta objects refer to the same type. + * @param other The meta object with which to compare. + * @return True if the two meta objects refer to the same type, false + * otherwise. + */ + bool operator==(const meta_type &other) const ENTT_NOEXCEPT { + return (!node && !other.node) || (node && other.node && node->type_id == other.node->type_id); + } + + /*! @brief Removes a meta object from the list of searchable types. */ + void detach() ENTT_NOEXCEPT { + internal::meta_context::detach(node); + } + +private: + const internal::meta_type_node *node; +}; + + +/** + * @brief Checks if two meta objects refer to the same type. + * @param lhs A meta object, either valid or not. + * @param rhs A meta object, either valid or not. + * @return False if the two meta objects refer to the same node, true otherwise. + */ +inline bool operator!=(const meta_type &lhs, const meta_type &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +inline meta_type meta_any::type() const ENTT_NOEXCEPT { + return node; +} + + +inline meta_type meta_base::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +inline meta_type meta_base::type() const ENTT_NOEXCEPT { + return node->type(); +} + + +inline meta_type meta_conv::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +inline meta_type meta_conv::type() const ENTT_NOEXCEPT { + return node->type(); +} + + +inline meta_type meta_ctor::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +inline meta_type meta_ctor::arg(size_type index) const ENTT_NOEXCEPT { + return index < size() ? node->arg(index) : nullptr; +} + + +inline meta_type meta_data::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +inline meta_type meta_data::type() const ENTT_NOEXCEPT { + return node->type(); +} + + +inline meta_type meta_func::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +inline meta_type meta_func::ret() const ENTT_NOEXCEPT { + return node->ret(); +} + + +inline meta_type meta_func::arg(size_type index) const ENTT_NOEXCEPT { + return index < size() ? node->arg(index) : nullptr; +} + + +} + + +#endif + +// #include "policy.hpp" +#ifndef ENTT_META_POLICY_HPP +#define ENTT_META_POLICY_HPP + + +namespace entt { + + +/*! @brief Empty class type used to request the _as ref_ policy. */ +struct as_ref_t {}; + + +/*! @brief Disambiguation tag. */ +inline constexpr as_ref_t as_ref; + + +/*! @copydoc as_ref_t */ +using as_alias_t [[deprecated("use as_ref_t instead")]] = as_ref_t; + + +/*! @copydoc as_ref */ +[[deprecated("use as_ref instead")]] +inline constexpr as_ref_t as_alias; + + +/*! @brief Empty class type used to request the _as-is_ policy. */ +struct as_is_t {}; + + +/*! @brief Empty class type used to request the _as void_ policy. */ +struct as_void_t {}; + + +} + + +#endif + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct meta_function_helper; + + +template +struct meta_function_helper { + using return_type = std::remove_cv_t>; + using args_type = std::tuple>...>; + + static constexpr std::index_sequence_for index_sequence{}; + static constexpr auto is_const = false; + + static auto arg(typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT { + return std::array{{meta_info::resolve()...}}[index]; + } +}; + + +template +struct meta_function_helper: meta_function_helper { + static constexpr auto is_const = true; +}; + + +template +constexpr meta_function_helper +to_meta_function_helper(Ret(Class:: *)(Args...)); + + +template +constexpr meta_function_helper +to_meta_function_helper(Ret(Class:: *)(Args...) const); + + +template +constexpr meta_function_helper +to_meta_function_helper(Ret(*)(Args...)); + + +constexpr void to_meta_function_helper(...); + + +template +using meta_function_helper_t = decltype(to_meta_function_helper(std::declval())); + + +template +meta_any construct(meta_any * const args, std::index_sequence) { + [[maybe_unused]] auto direct = std::make_tuple((args+Indexes)->try_cast()...); + meta_any any{}; + + if(((std::get(direct) || (args+Indexes)->convert()) && ...)) { + any = Type{(std::get(direct) ? *std::get(direct) : (args+Indexes)->cast())...}; + } + + return any; +} + + +template +bool setter([[maybe_unused]] meta_any instance, [[maybe_unused]] meta_any index, [[maybe_unused]] meta_any value) { + bool accepted = false; + + if constexpr(!Const) { + if constexpr(std::is_function_v>> || std::is_member_function_pointer_v) { + using helper_type = meta_function_helper_t; + using data_type = std::tuple_element_t, typename helper_type::args_type>; + static_assert(std::is_invocable_v); + auto * const clazz = instance.try_cast(); + auto * const direct = value.try_cast(); + + if(clazz && (direct || value.convert())) { + std::invoke(Data, *clazz, direct ? *direct : value.cast()); + accepted = true; + } + } else if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_cv_t().*Data)>>; + static_assert(std::is_invocable_v); + auto * const clazz = instance.try_cast(); + + if constexpr(std::is_array_v) { + using underlying_type = std::remove_extent_t; + auto * const direct = value.try_cast(); + auto * const idx = index.try_cast(); + + if(clazz && idx && (direct || value.convert())) { + std::invoke(Data, clazz)[*idx] = direct ? *direct : value.cast(); + accepted = true; + } + } else { + auto * const direct = value.try_cast(); + + if(clazz && (direct || value.convert())) { + std::invoke(Data, clazz) = (direct ? *direct : value.cast()); + accepted = true; + } + } + } else { + static_assert(std::is_pointer_v); + using data_type = std::remove_cv_t>; + + if constexpr(std::is_array_v) { + using underlying_type = std::remove_extent_t; + auto * const direct = value.try_cast(); + auto * const idx = index.try_cast(); + + if(idx && (direct || value.convert())) { + (*Data)[*idx] = (direct ? *direct : value.cast()); + accepted = true; + } + } else { + auto * const direct = value.try_cast(); + + if(direct || value.convert()) { + *Data = (direct ? *direct : value.cast()); + accepted = true; + } + } + } + } + + return accepted; +} + + +template +meta_any getter([[maybe_unused]] meta_any instance, [[maybe_unused]] meta_any index) { + auto dispatch = [](auto &&value) { + if constexpr(std::is_same_v) { + return meta_any{std::in_place_type, std::forward(value)}; + } else if constexpr(std::is_same_v) { + return meta_any{std::ref(std::forward(value))}; + } else { + static_assert(std::is_same_v); + return meta_any{std::forward(value)}; + } + }; + + if constexpr(std::is_function_v>> || std::is_member_function_pointer_v) { + static_assert(std::is_invocable_v); + auto * const clazz = instance.try_cast(); + return clazz ? dispatch(std::invoke(Data, *clazz)) : meta_any{}; + } else if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_cv_t().*Data)>>; + static_assert(std::is_invocable_v); + auto * const clazz = instance.try_cast(); + + if constexpr(std::is_array_v) { + auto * const idx = index.try_cast(); + return (clazz && idx) ? dispatch(std::invoke(Data, clazz)[*idx]) : meta_any{}; + } else { + return clazz ? dispatch(std::invoke(Data, clazz)) : meta_any{}; + } + } else { + static_assert(std::is_pointer_v>); + + if constexpr(std::is_array_v>) { + auto * const idx = index.try_cast(); + return idx ? dispatch((*Data)[*idx]) : meta_any{}; + } else { + return dispatch(*Data); + } + } +} + + +template +meta_any invoke([[maybe_unused]] meta_any instance, meta_any *args, std::index_sequence) { + using helper_type = meta_function_helper_t; + + auto dispatch = [](auto *... params) { + if constexpr(std::is_void_v || std::is_same_v) { + std::invoke(Candidate, *params...); + return meta_any{std::in_place_type}; + } else if constexpr(std::is_same_v) { + return meta_any{std::ref(std::invoke(Candidate, *params...))}; + } else { + static_assert(std::is_same_v); + return meta_any{std::invoke(Candidate, *params...)}; + } + }; + + [[maybe_unused]] const auto direct = std::make_tuple([](meta_any *any, auto *value) { + using arg_type = std::remove_reference_t; + + if(!value && any->convert()) { + value = any->try_cast(); + } + + return value; + }(args+Indexes, (args+Indexes)->try_cast>())...); + + if constexpr(std::is_function_v>>) { + return (std::get(direct) && ...) ? dispatch(std::get(direct)...) : meta_any{}; + } else { + auto * const clazz = instance.try_cast(); + return (clazz && (std::get(direct) && ...)) ? dispatch(clazz, std::get(direct)...) : meta_any{}; + } +} + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/** + * @brief Meta factory to be used for reflection purposes. + * + * The meta factory is an utility class used to reflect types, data members and + * functions of all sorts. This class ensures that the underlying web of types + * is built correctly and performs some checks in debug mode to ensure that + * there are no subtle errors at runtime. + */ +template +class meta_factory; + + +/** + * @brief Extended meta factory to be used for reflection purposes. + * @tparam Type Reflected type for which the factory was created. + * @tparam Spec Property specialization pack used to disambiguate overloads. + */ +template +class meta_factory: public meta_factory { + bool exists(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT { + return node && (node->key() == key || exists(key, node->next)); + } + + template + void unpack(std::index_sequence, std::tuple property, Other &&... other) { + unroll(choice<3>, std::move(std::get(property))..., std::forward(other)...); + } + + template + void unroll(choice_t<3>, std::tuple property, Other &&... other) { + unpack(std::index_sequence_for{}, std::move(property), std::forward(other)...); + } + + template + void unroll(choice_t<2>, std::pair property, Other &&... other) { + assign(std::move(property.first), std::move(property.second)); + unroll(choice<3>, std::forward(other)...); + } + + template + std::enable_if_t> + unroll(choice_t<1>, Property &&property, Other &&... other) { + assign(std::forward(property)); + unroll(choice<3>, std::forward(other)...); + } + + template + void unroll(choice_t<0>, Func &&invocable, Other &&... other) { + unroll(choice<3>, std::forward(invocable)(), std::forward(other)...); + } + + template + void unroll(choice_t<0>) {} + + template + void assign(Key &&key, Value &&... value) { + static const auto property{std::make_tuple(std::forward(key), std::forward(value)...)}; + + static internal::meta_prop_node node{ + nullptr, + []() -> meta_any { + return std::get<0>(property); + }, + []() -> meta_any { + if constexpr(sizeof...(Value) == 0) { + return {}; + } else { + return std::get<1>(property); + } + } + }; + + ENTT_ASSERT(!exists(node.key(), *curr)); + node.next = *curr; + *curr = &node; + } + +public: + /** + * @brief Constructs an extended factory from a given node. + * @param target The underlying node to which to assign the properties. + */ + meta_factory(entt::internal::meta_prop_node **target) ENTT_NOEXCEPT + : curr{target} + {} + + /** + * @brief Assigns a property to the last meta object created. + * + * Both the key and the value (if any) must be at least copy constructible. + * + * @tparam PropertyOrKey Type of the property or property key. + * @tparam Value Optional type of the property value. + * @param property_or_key Property or property key. + * @param value Optional property value. + * @return A meta factory for the parent type. + */ + template + auto prop(PropertyOrKey &&property_or_key, Value &&... value) && { + if constexpr(sizeof...(Value) == 0) { + unroll(choice<3>, std::forward(property_or_key)); + } else { + assign(std::forward(property_or_key), std::forward(value)...); + } + + return meta_factory{curr}; + } + + /** + * @brief Assigns properties to the last meta object created. + * + * Both the keys and the values (if any) must be at least copy + * constructible. + * + * @tparam Property Types of the properties. + * @param property Properties to assign to the last meta object created. + * @return A meta factory for the parent type. + */ + template + auto props(Property... property) && { + unroll(choice<3>, std::forward(property)...); + return meta_factory{curr}; + } + +private: + entt::internal::meta_prop_node **curr; +}; + + +/** + * @brief Basic meta factory to be used for reflection purposes. + * @tparam Type Reflected type for which the factory was created. + */ +template +class meta_factory { + template + bool exists(const Node *candidate, const Node *node) ENTT_NOEXCEPT { + return node && (node == candidate || exists(candidate, node->next)); + } + + template + bool exists(const id_type id, const Node *node) ENTT_NOEXCEPT { + return node && (node->id == id || exists(id, node->next)); + } + +public: + /** + * @brief Makes a meta type _searchable_. + * @param id Optional unique identifier. + * @return An extended meta factory for the given type. + */ + auto type(const id_type id = type_info::id()) { + auto * const node = internal::meta_info::resolve(); + + ENTT_ASSERT(!exists(id, *internal::meta_context::global)); + ENTT_ASSERT(!exists(node, *internal::meta_context::global)); + node->id = id; + node->next = *internal::meta_context::global; + *internal::meta_context::global = node; + + return meta_factory{&node->prop}; + } + + /*! @copydoc type */ + [[deprecated("use ::type instead")]] + auto alias(const id_type id) ENTT_NOEXCEPT { + return type(id); + } + + /** + * @brief Assigns a meta base to a meta type. + * + * A reflected base class must be a real base class of the reflected type. + * + * @tparam Base Type of the base class to assign to the meta type. + * @return A meta factory for the parent type. + */ + template + auto base() ENTT_NOEXCEPT { + static_assert(std::is_base_of_v); + auto * const type = internal::meta_info::resolve(); + + static internal::meta_base_node node{ + type, + nullptr, + &internal::meta_info::resolve, + [](void *instance) ENTT_NOEXCEPT -> void * { + return static_cast(static_cast(instance)); + } + }; + + ENTT_ASSERT(!exists(&node, type->base)); + node.next = type->base; + type->base = &node; + + return meta_factory{}; + } + + /** + * @brief Assigns a meta conversion function to a meta type. + * + * The given type must be such that an instance of the reflected type can be + * converted to it. + * + * @tparam To Type of the conversion function to assign to the meta type. + * @return A meta factory for the parent type. + */ + template + auto conv() ENTT_NOEXCEPT { + static_assert(std::is_convertible_v); + auto * const type = internal::meta_info::resolve(); + + static internal::meta_conv_node node{ + type, + nullptr, + &internal::meta_info::resolve, + [](const void *instance) -> meta_any { + return static_cast(*static_cast(instance)); + } + }; + + ENTT_ASSERT(!exists(&node, type->conv)); + node.next = type->conv; + type->conv = &node; + + return meta_factory{}; + } + + /** + * @brief Assigns a meta conversion function to a meta type. + * + * Conversion functions can be either free functions or member + * functions.
+ * In case of free functions, they must accept a const reference to an + * instance of the parent type as an argument. In case of member functions, + * they should have no arguments at all. + * + * @tparam Candidate The actual function to use for the conversion. + * @return A meta factory for the parent type. + */ + template + auto conv() ENTT_NOEXCEPT { + using conv_type = std::invoke_result_t; + auto * const type = internal::meta_info::resolve(); + + static internal::meta_conv_node node{ + type, + nullptr, + &internal::meta_info::resolve, + [](const void *instance) -> meta_any { + return std::invoke(Candidate, *static_cast(instance)); + } + }; + + ENTT_ASSERT(!exists(&node, type->conv)); + node.next = type->conv; + type->conv = &node; + + return meta_factory{}; + } + + /** + * @brief Assigns a meta constructor to a meta type. + * + * Free functions can be assigned to meta types in the role of constructors. + * All that is required is that they return an instance of the underlying + * type.
+ * From a client's point of view, nothing changes if a constructor of a meta + * type is a built-in one or a free function. + * + * @tparam Func The actual function to use as a constructor. + * @tparam Policy Optional policy (no policy set by default). + * @return An extended meta factory for the parent type. + */ + template + auto ctor() ENTT_NOEXCEPT { + using helper_type = internal::meta_function_helper_t; + static_assert(std::is_same_v); + auto * const type = internal::meta_info::resolve(); + + static internal::meta_ctor_node node{ + type, + nullptr, + nullptr, + helper_type::index_sequence.size(), + &helper_type::arg, + [](meta_any * const any) { + return internal::invoke({}, any, helper_type::index_sequence); + } + }; + + ENTT_ASSERT(!exists(&node, type->ctor)); + node.next = type->ctor; + type->ctor = &node; + + return meta_factory>{&node.prop}; + } + + /** + * @brief Assigns a meta constructor to a meta type. + * + * A meta constructor is uniquely identified by the types of its arguments + * and is such that there exists an actual constructor of the underlying + * type that can be invoked with parameters whose types are those given. + * + * @tparam Args Types of arguments to use to construct an instance. + * @return An extended meta factory for the parent type. + */ + template + auto ctor() ENTT_NOEXCEPT { + using helper_type = internal::meta_function_helper_t; + auto * const type = internal::meta_info::resolve(); + + static internal::meta_ctor_node node{ + type, + nullptr, + nullptr, + helper_type::index_sequence.size(), + &helper_type::arg, + [](meta_any * const any) { + return internal::construct>...>(any, helper_type::index_sequence); + } + }; + + ENTT_ASSERT(!exists(&node, type->ctor)); + node.next = type->ctor; + type->ctor = &node; + + return meta_factory{&node.prop}; + } + + /** + * @brief Assigns a meta destructor to a meta type. + * + * Free functions can be assigned to meta types in the role of destructors. + * The signature of the function should identical to the following: + * + * @code{.cpp} + * void(Type &); + * @endcode + * + * The purpose is to give users the ability to free up resources that + * require special treatment before an object is actually destroyed. + * + * @tparam Func The actual function to use as a destructor. + * @return A meta factory for the parent type. + */ + template + auto dtor() ENTT_NOEXCEPT { + static_assert(std::is_invocable_v); + auto * const type = internal::meta_info::resolve(); + + static internal::meta_dtor_node node{ + type, + [](void *instance) { + if(instance) { + std::invoke(Func, *static_cast(instance)); + } + } + }; + + ENTT_ASSERT(!type->dtor); + type->dtor = &node; + + return meta_factory{}; + } + + /** + * @brief Assigns a meta data to a meta type. + * + * Both data members and static and global variables, as well as constants + * of any kind, can be assigned to a meta type.
+ * From a client's point of view, all the variables associated with the + * reflected object will appear as if they were part of the type itself. + * + * @tparam Data The actual variable to attach to the meta type. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return An extended meta factory for the parent type. + */ + template + auto data(const id_type id) ENTT_NOEXCEPT { + auto * const type = internal::meta_info::resolve(); + internal::meta_data_node *curr = nullptr; + + if constexpr(std::is_same_v) { + static_assert(std::is_same_v); + + static internal::meta_data_node node{ + {}, + type, + nullptr, + nullptr, + true, + true, + &internal::meta_info::resolve, + [](meta_any, meta_any, meta_any) { return false; }, + [](meta_any, meta_any) -> meta_any { return Data; } + }; + + curr = &node; + } else if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_reference_t().*Data)>; + + static internal::meta_data_node node{ + {}, + type, + nullptr, + nullptr, + std::is_const_v, + !std::is_member_object_pointer_v, + &internal::meta_info::resolve, + &internal::setter, Type, Data>, + &internal::getter + }; + + curr = &node; + } else { + static_assert(std::is_pointer_v>); + using data_type = std::remove_pointer_t>; + + static internal::meta_data_node node{ + {}, + type, + nullptr, + nullptr, + std::is_const_v, + !std::is_member_object_pointer_v, + &internal::meta_info::resolve, + &internal::setter, Type, Data>, + &internal::getter + }; + + curr = &node; + } + + ENTT_ASSERT(!exists(id, type->data)); + ENTT_ASSERT(!exists(curr, type->data)); + curr->id = id; + curr->next = type->data; + type->data = curr; + + return meta_factory>{&curr->prop}; + } + + /** + * @brief Assigns a meta data to a meta type by means of its setter and + * getter. + * + * Setters and getters can be either free functions, member functions or a + * mix of them.
+ * In case of free functions, setters and getters must accept a reference to + * an instance of the parent type as their first argument. A setter has then + * an extra argument of a type convertible to that of the parameter to + * set.
+ * In case of member functions, getters have no arguments at all, while + * setters has an argument of a type convertible to that of the parameter to + * set. + * + * @tparam Setter The actual function to use as a setter. + * @tparam Getter The actual function to use as a getter. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return An extended meta factory for the parent type. + */ + template + auto data(const id_type id) ENTT_NOEXCEPT { + using underlying_type = std::invoke_result_t; + static_assert(std::is_invocable_v); + auto * const type = internal::meta_info::resolve(); + + static internal::meta_data_node node{ + {}, + type, + nullptr, + nullptr, + false, + false, + &internal::meta_info::resolve, + &internal::setter, + &internal::getter + }; + + ENTT_ASSERT(!exists(id, type->data)); + ENTT_ASSERT(!exists(&node, type->data)); + node.id = id; + node.next = type->data; + type->data = &node; + + return meta_factory, std::integral_constant>{&node.prop}; + } + + /** + * @brief Assigns a meta funcion to a meta type. + * + * Both member functions and free functions can be assigned to a meta + * type.
+ * From a client's point of view, all the functions associated with the + * reflected object will appear as if they were part of the type itself. + * + * @tparam Candidate The actual function to attach to the meta type. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return An extended meta factory for the parent type. + */ + template + auto func(const id_type id) ENTT_NOEXCEPT { + using helper_type = internal::meta_function_helper_t; + auto * const type = internal::meta_info::resolve(); + + static internal::meta_func_node node{ + {}, + type, + nullptr, + nullptr, + helper_type::index_sequence.size(), + helper_type::is_const, + !std::is_member_function_pointer_v, + &internal::meta_info, void, typename helper_type::return_type>>::resolve, + &helper_type::arg, + [](meta_any instance, meta_any *args) { + return internal::invoke(std::move(instance), args, helper_type::index_sequence); + } + }; + + ENTT_ASSERT(!exists(id, type->func)); + ENTT_ASSERT(!exists(&node, type->func)); + node.id = id; + node.next = type->func; + type->func = &node; + + return meta_factory>{&node.prop}; + } + + /** + * @brief Resets a meta type and all its parts. + * + * This function resets a meta type and all its data members, member + * functions and properties, as well as its constructors, destructors and + * conversion functions if any.
+ * Base classes aren't reset but the link between the two types is removed. + * + * @return An extended meta factory for the given type. + */ + auto reset() ENTT_NOEXCEPT { + auto * const node = internal::meta_info::resolve(); + + internal::meta_context::detach(node); + + const auto unregister_all = y_combinator{ + [](auto &&self, auto **curr, auto... member) { + while(*curr) { + auto *prev = *curr; + (self(&(prev->*member)), ...); + *curr = prev->next; + prev->next = nullptr; + } + } + }; + + unregister_all(&node->prop); + unregister_all(&node->base); + unregister_all(&node->conv); + unregister_all(&node->ctor, &internal::meta_ctor_node::prop); + unregister_all(&node->data, &internal::meta_data_node::prop); + unregister_all(&node->func, &internal::meta_func_node::prop); + + node->id = {}; + node->next = nullptr; + node->dtor = nullptr; + + return meta_factory{&node->prop}; + } +}; + + +/** + * @brief Utility function to use for reflection. + * + * This is the point from which everything starts.
+ * By invoking this function with a type that is not yet reflected, a meta type + * is created to which it will be possible to attach meta objects through a + * dedicated factory. + * + * @tparam Type Type to reflect. + * @return An meta factory for the given type. + */ +template +inline meta_factory meta() ENTT_NOEXCEPT { + auto * const node = internal::meta_info::resolve(); + // extended meta factory to allow assigning properties to opaque meta types + return meta_factory{&node->prop}; +} + + +} + + +#endif + +// #include "meta/meta.hpp" + +// #include "meta/resolve.hpp" +#ifndef ENTT_META_RESOLVE_HPP +#define ENTT_META_RESOLVE_HPP + + +#include +// #include "meta.hpp" + + + +namespace entt { + + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @return The meta type associated with the given type, if any. + */ +template +inline meta_type resolve() ENTT_NOEXCEPT { + return internal::meta_info::resolve(); +} + + +/** + * @brief Returns the first meta type that satisfies specific criteria, if any. + * @tparam Func Type of the unary predicate to use to test the meta types. + * @param func Unary predicate which returns ​true for the required element. + * @return The first meta type satisfying the condition, if any. + */ +template +inline meta_type resolve_if(Func func) ENTT_NOEXCEPT { + return internal::find_if([&func](const auto *curr) { + return func(meta_type{curr}); + }, *internal::meta_context::global); +} + + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +inline meta_type resolve_id(const id_type id) ENTT_NOEXCEPT { + return resolve_if([id](const auto type) { return type.id() == id; }); +} + + +/** + * @brief Returns the meta type associated with a given type id, if any. + * @param id Unique identifier. + * @return The meta type associated with the given type id, if any. + */ +inline meta_type resolve_type(const id_type id) ENTT_NOEXCEPT { + return resolve_if([id](const auto type) { return type.type_id() == id; }); +} + + +/*! @copydoc resolve_id */ +[[deprecated("use entt::resolve_id instead")]] +inline meta_type resolve(const id_type id) ENTT_NOEXCEPT { + return resolve_id(id); +} + + +/** + * @brief Iterates all the reflected types. + * @tparam Op Type of the function object to invoke. + * @param op A valid function object. + */ +template +inline std::enable_if_t, void> +resolve(Op op) { + internal::visit(op, *internal::meta_context::global); +} + + +} + + +#endif + +// #include "meta/policy.hpp" + +// #include "process/process.hpp" +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty_v +#else +# include +# // sfinae-friendly definition +# define ENTT_IS_EMPTY(Type) (false && std::is_empty_v) +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) __VA_ARGS__ +# endif +#endif + + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty_v +#else +# include +# // sfinae-friendly definition +# define ENTT_IS_EMPTY(Type) (false && std::is_empty_v) +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) __VA_ARGS__ +# endif +#endif + + +#endif + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +// #include "../config/config.h" + + + +namespace entt { + + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + + +} + + +#endif + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct fnv1a_traits; + + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string { + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {} + const Char *str; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT { + auto value = traits_type::offset; + + while(*curr != 0) { + value = (value ^ static_cast(*(curr++))) * traits_type::prime; + } + + return value; + } + +public: + /*! @brief Character type. */ + using value_type = Char; + /*! @brief Unsigned integer type. */ + using hash_type = id_type; + + /** + * @brief Returns directly the numeric representation of a string. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * const auto value = basic_hashed_string::to_value("my.png"); + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + * @return The numeric representation of the string. + */ + template + static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); + } + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_NOEXCEPT + : str{nullptr}, hash{} + {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * basic_hashed_string hs{"my.png"}; + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param curr Human-readable identifer. + */ + template + constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT + : str{curr}, hash{helper(curr)} + {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + : str{wrapper.str}, hash{helper(wrapper.str)} + {} + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the instance. + */ + constexpr const value_type * data() const ENTT_NOEXCEPT { + return str; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + constexpr hash_type value() const ENTT_NOEXCEPT { + return hash; + } + + /*! @copydoc data */ + constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } + + /** + * @brief Compares two hashed strings. + * @param other Hashed string with which to compare. + * @return True if the two hashed strings are identical, false otherwise. + */ + constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + return hash == other.hash; + } + +private: + const value_type *str; + hash_type hash; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the character type of the hashed string directly from a + * human-readable identifer provided to the constructor. + * + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + */ +template +basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT +-> basic_hashed_string; + + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template +constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + + +} + + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_string{str}; +} + + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_wstring{str}; +} + + +#endif + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + + +/** + * @brief Alias template to ease the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + /*! @cond TURN_OFF_DOXYGEN */ + : choice_t + /*! @endcond TURN_OFF_DOXYGEN */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + + +/*! @brief A class to use to push around lists of types, nothing more. */ +template +struct type_list {}; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_size; + + +/** + * @brief Compile-time number of elements in a type list. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_size> + : std::integral_constant +{}; + + +/** + * @brief Helper variable template. + * @tparam List Type list. + */ +template +inline constexpr auto type_list_size_v = type_list_size::value; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + std::disjunction_v...>, + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type> + >; +}; + + +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; + + +/** + * @brief Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template> +struct is_equality_comparable: std::false_type {}; + + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +inline constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v); + + template + static Class * clazz(Ret(Class:: *)(Args...)); + + template + static Class * clazz(Ret(Class:: *)(Args...) const); + + template + static Class * clazz(Type Class:: *); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + + +} + + +/** + * @brief Defines an enum class to use for opaque identifiers and a dedicate + * `to_integer` function to convert the identifiers to their underlying type. + * @param clazz The name to use for the enum class. + * @param type The underlying type for the enum class. + */ +#define ENTT_OPAQUE_TYPE(clazz, type)\ + enum class clazz: type {};\ + constexpr auto to_integral(const clazz id) ENTT_NOEXCEPT {\ + return static_cast>(id);\ + }\ + static_assert(true) + + +#endif + + + +namespace entt { + + +/** + * @brief Base class for processes. + * + * This class stays true to the CRTP idiom. Derived classes must specify what's + * the intended type for elapsed times.
+ * A process should expose publicly the following member functions whether + * required: + * + * * @code{.cpp} + * void update(Delta, void *); + * @endcode + * + * It's invoked once per tick until a process is explicitly aborted or it + * terminates either with or without errors. Even though it's not mandatory to + * declare this member function, as a rule of thumb each process should at + * least define it to work properly. The `void *` parameter is an opaque + * pointer to user data (if any) forwarded directly to the process during an + * update. + * + * * @code{.cpp} + * void init(); + * @endcode + * + * It's invoked when the process joins the running queue of a scheduler. This + * happens as soon as it's attached to the scheduler if the process is a top + * level one, otherwise when it replaces its parent if the process is a + * continuation. + * + * * @code{.cpp} + * void succeeded(); + * @endcode + * + * It's invoked in case of success, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void failed(); + * @endcode + * + * It's invoked in case of errors, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void aborted(); + * @endcode + * + * It's invoked only if a process is explicitly aborted. There is no guarantee + * that it executes in the same tick, this depends solely on whether the + * process is aborted immediately or not. + * + * Derived classes can change the internal state of a process by invoking the + * `succeed` and `fail` protected member functions and even pause or unpause the + * process itself. + * + * @sa scheduler + * + * @tparam Derived Actual type of process that extends the class template. + * @tparam Delta Type to use to provide elapsed time. + */ +template +class process { + enum class state: unsigned int { + UNINITIALIZED = 0, + RUNNING, + PAUSED, + SUCCEEDED, + FAILED, + ABORTED, + FINISHED + }; + + template + auto next(integral_constant) + -> decltype(std::declval().init()) { + static_cast(this)->init(); + } + + template + auto next(integral_constant, Delta delta, void *data) + -> decltype(std::declval().update(delta, data)) { + static_cast(this)->update(delta, data); + } + + template + auto next(integral_constant) + -> decltype(std::declval().succeeded()) { + static_cast(this)->succeeded(); + } + + template + auto next(integral_constant) + -> decltype(std::declval().failed()) { + static_cast(this)->failed(); + } + + template + auto next(integral_constant) + -> decltype(std::declval().aborted()) { + static_cast(this)->aborted(); + } + + void next(...) const ENTT_NOEXCEPT {} + +protected: + /** + * @brief Terminates a process with success if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void succeed() ENTT_NOEXCEPT { + if(alive()) { + current = state::SUCCEEDED; + } + } + + /** + * @brief Terminates a process with errors if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void fail() ENTT_NOEXCEPT { + if(alive()) { + current = state::FAILED; + } + } + + /** + * @brief Stops a process if it's in a running state. + * + * The function is idempotent and it does nothing if the process isn't + * running. + */ + void pause() ENTT_NOEXCEPT { + if(current == state::RUNNING) { + current = state::PAUSED; + } + } + + /** + * @brief Restarts a process if it's paused. + * + * The function is idempotent and it does nothing if the process isn't + * paused. + */ + void unpause() ENTT_NOEXCEPT { + if(current == state::PAUSED) { + current = state::RUNNING; + } + } + +public: + /*! @brief Type used to provide elapsed time. */ + using delta_type = Delta; + + /*! @brief Default destructor. */ + virtual ~process() { + static_assert(std::is_base_of_v); + } + + /** + * @brief Aborts a process if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + * + * @param immediately Requests an immediate operation. + */ + void abort(const bool immediately = false) { + if(alive()) { + current = state::ABORTED; + + if(immediately) { + tick({}); + } + } + } + + /** + * @brief Returns true if a process is either running or paused. + * @return True if the process is still alive, false otherwise. + */ + bool alive() const ENTT_NOEXCEPT { + return current == state::RUNNING || current == state::PAUSED; + } + + /** + * @brief Returns true if a process is already terminated. + * @return True if the process is terminated, false otherwise. + */ + bool dead() const ENTT_NOEXCEPT { + return current == state::FINISHED; + } + + /** + * @brief Returns true if a process is currently paused. + * @return True if the process is paused, false otherwise. + */ + bool paused() const ENTT_NOEXCEPT { + return current == state::PAUSED; + } + + /** + * @brief Returns true if a process terminated with errors. + * @return True if the process terminated with errors, false otherwise. + */ + bool rejected() const ENTT_NOEXCEPT { + return stopped; + } + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void tick(const Delta delta, void *data = nullptr) { + switch (current) { + case state::UNINITIALIZED: + next(integral_constant{}); + current = state::RUNNING; + break; + case state::RUNNING: + next(integral_constant{}, delta, data); + break; + default: + // suppress warnings + break; + } + + // if it's dead, it must be notified and removed immediately + switch(current) { + case state::SUCCEEDED: + next(integral_constant{}); + current = state::FINISHED; + break; + case state::FAILED: + next(integral_constant{}); + current = state::FINISHED; + stopped = true; + break; + case state::ABORTED: + next(integral_constant{}); + current = state::FINISHED; + stopped = true; + break; + default: + // suppress warnings + break; + } + } + +private: + state current{state::UNINITIALIZED}; + bool stopped{false}; +}; + + +/** + * @brief Adaptor for lambdas and functors to turn them into processes. + * + * Lambdas and functors can't be used directly with a scheduler for they are not + * properly defined processes with managed life cycles.
+ * This class helps in filling the gap and turning lambdas and functors into + * full featured processes usable by a scheduler. + * + * The signature of the function call operator should be equivalent to the + * following: + * + * @code{.cpp} + * void(Delta delta, void *data, auto succeed, auto fail); + * @endcode + * + * Where: + * + * * `delta` is the elapsed time. + * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. + * * `succeed` is a function to call when a process terminates with success. + * * `fail` is a function to call when a process terminates with errors. + * + * The signature of the function call operator of both `succeed` and `fail` + * is equivalent to the following: + * + * @code{.cpp} + * void(); + * @endcode + * + * Usually users shouldn't worry about creating adaptors. A scheduler will + * create them internally each and avery time a lambda or a functor is used as + * a process. + * + * @sa process + * @sa scheduler + * + * @tparam Func Actual type of process. + * @tparam Delta Type to use to provide elapsed time. + */ +template +struct process_adaptor: process, Delta>, private Func { + /** + * @brief Constructs a process adaptor from a lambda or a functor. + * @tparam Args Types of arguments to use to initialize the actual process. + * @param args Parameters to use to initialize the actual process. + */ + template + process_adaptor(Args &&... args) + : Func{std::forward(args)...} + {} + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void update(const Delta delta, void *data) { + Func::operator()(delta, data, [this]() { this->succeed(); }, [this]() { this->fail(); }); + } +}; + + +} + + +#endif + +// #include "process/scheduler.hpp" +#ifndef ENTT_PROCESS_SCHEDULER_HPP +#define ENTT_PROCESS_SCHEDULER_HPP + + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "process.hpp" + + + +namespace entt { + + +/** + * @brief Cooperative scheduler for processes. + * + * A cooperative scheduler runs processes and helps managing their life cycles. + * + * Each process is invoked once per tick. If a process terminates, it's + * removed automatically from the scheduler and it's never invoked again.
+ * A process can also have a child. In this case, the process is replaced with + * its child when it terminates if it returns with success. In case of errors, + * both the process and its child are discarded. + * + * Example of use (pseudocode): + * + * @code{.cpp} + * scheduler.attach([](auto delta, void *, auto succeed, auto fail) { + * // code + * }).then(arguments...); + * @endcode + * + * In order to invoke all scheduled processes, call the `update` member function + * passing it the elapsed time to forward to the tasks. + * + * @sa process + * + * @tparam Delta Type to use to provide elapsed time. + */ +template +class scheduler { + struct process_handler { + using instance_type = std::unique_ptr; + using update_fn_type = bool(process_handler &, Delta, void *); + using abort_fn_type = void(process_handler &, bool); + using next_type = std::unique_ptr; + + instance_type instance; + update_fn_type *update; + abort_fn_type *abort; + next_type next; + }; + + struct continuation { + continuation(process_handler *ref) + : handler{ref} + { + ENTT_ASSERT(handler); + } + + template + continuation then(Args &&... args) { + static_assert(std::is_base_of_v, Proc>); + auto proc = typename process_handler::instance_type{new Proc{std::forward(args)...}, &scheduler::deleter}; + handler->next.reset(new process_handler{std::move(proc), &scheduler::update, &scheduler::abort, nullptr}); + handler = handler->next.get(); + return *this; + } + + template + continuation then(Func &&func) { + return then, Delta>>(std::forward(func)); + } + + private: + process_handler *handler; + }; + + template + static bool update(process_handler &handler, const Delta delta, void *data) { + auto *process = static_cast(handler.instance.get()); + process->tick(delta, data); + + auto dead = process->dead(); + + if(dead) { + if(handler.next && !process->rejected()) { + handler = std::move(*handler.next); + // forces the process to exit the uninitialized state + dead = handler.update(handler, {}, nullptr); + } else { + handler.instance.reset(); + } + } + + return dead; + } + + template + static void abort(process_handler &handler, const bool immediately) { + static_cast(handler.instance.get())->abort(immediately); + } + + template + static void deleter(void *proc) { + delete static_cast(proc); + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /*! @brief Default constructor. */ + scheduler() = default; + + /*! @brief Default move constructor. */ + scheduler(scheduler &&) = default; + + /*! @brief Default move assignment operator. @return This scheduler. */ + scheduler & operator=(scheduler &&) = default; + + /** + * @brief Number of processes currently scheduled. + * @return Number of processes currently scheduled. + */ + size_type size() const ENTT_NOEXCEPT { + return handlers.size(); + } + + /** + * @brief Returns true if at least a process is currently scheduled. + * @return True if there are scheduled processes, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return handlers.empty(); + } + + /** + * @brief Discards all scheduled processes. + * + * Processes aren't aborted. They are discarded along with their children + * and never executed again. + */ + void clear() { + handlers.clear(); + } + + /** + * @brief Schedules a process for the next tick. + * + * Returned value is an opaque object that can be used to attach a child to + * the given process. The child is automatically scheduled when the process + * terminates and only if the process returns with success. + * + * Example of use (pseudocode): + * + * @code{.cpp} + * // schedules a task in the form of a process class + * scheduler.attach(arguments...) + * // appends a child in the form of a lambda function + * .then([](auto delta, void *, auto succeed, auto fail) { + * // code + * }) + * // appends a child in the form of another process class + * .then(); + * @endcode + * + * @tparam Proc Type of process to schedule. + * @tparam Args Types of arguments to use to initialize the process. + * @param args Parameters to use to initialize the process. + * @return An opaque object to use to concatenate processes. + */ + template + auto attach(Args &&... args) { + static_assert(std::is_base_of_v, Proc>); + auto proc = typename process_handler::instance_type{new Proc{std::forward(args)...}, &scheduler::deleter}; + process_handler handler{std::move(proc), &scheduler::update, &scheduler::abort, nullptr}; + // forces the process to exit the uninitialized state + handler.update(handler, {}, nullptr); + return continuation{&handlers.emplace_back(std::move(handler))}; + } + + /** + * @brief Schedules a process for the next tick. + * + * A process can be either a lambda or a functor. The scheduler wraps both + * of them in a process adaptor internally.
+ * The signature of the function call operator should be equivalent to the + * following: + * + * @code{.cpp} + * void(Delta delta, void *data, auto succeed, auto fail); + * @endcode + * + * Where: + * + * * `delta` is the elapsed time. + * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. + * * `succeed` is a function to call when a process terminates with success. + * * `fail` is a function to call when a process terminates with errors. + * + * The signature of the function call operator of both `succeed` and `fail` + * is equivalent to the following: + * + * @code{.cpp} + * void(); + * @endcode + * + * Returned value is an opaque object that can be used to attach a child to + * the given process. The child is automatically scheduled when the process + * terminates and only if the process returns with success. + * + * Example of use (pseudocode): + * + * @code{.cpp} + * // schedules a task in the form of a lambda function + * scheduler.attach([](auto delta, void *, auto succeed, auto fail) { + * // code + * }) + * // appends a child in the form of another lambda function + * .then([](auto delta, void *, auto succeed, auto fail) { + * // code + * }) + * // appends a child in the form of a process class + * .then(arguments...); + * @endcode + * + * @sa process_adaptor + * + * @tparam Func Type of process to schedule. + * @param func Either a lambda or a functor to use as a process. + * @return An opaque object to use to concatenate processes. + */ + template + auto attach(Func &&func) { + using Proc = process_adaptor, Delta>; + return attach(std::forward(func)); + } + + /** + * @brief Updates all scheduled processes. + * + * All scheduled processes are executed in no specific order.
+ * If a process terminates with success, it's replaced with its child, if + * any. Otherwise, if a process terminates with an error, it's removed along + * with its child. + * + * @param delta Elapsed time. + * @param data Optional data. + */ + void update(const Delta delta, void *data = nullptr) { + bool clean = false; + + for(auto pos = handlers.size(); pos; --pos) { + auto &handler = handlers[pos-1]; + const bool dead = handler.update(handler, delta, data); + clean = clean || dead; + } + + if(clean) { + handlers.erase(std::remove_if(handlers.begin(), handlers.end(), [](auto &handler) { + return !handler.instance; + }), handlers.end()); + } + } + + /** + * @brief Aborts all scheduled processes. + * + * Unless an immediate operation is requested, the abort is scheduled for + * the next tick. Processes won't be executed anymore in any case.
+ * Once a process is fully aborted and thus finished, it's discarded along + * with its child, if any. + * + * @param immediately Requests an immediate operation. + */ + void abort(const bool immediately = false) { + decltype(handlers) exec; + exec.swap(handlers); + + for(auto &&handler: exec) { + handler.abort(handler, immediately); + } + + std::move(handlers.begin(), handlers.end(), std::back_inserter(exec)); + handlers.swap(exec); + } + +private: + std::vector handlers{}; +}; + + +} + + +#endif + +// #include "resource/cache.hpp" +#ifndef ENTT_RESOURCE_CACHE_HPP +#define ENTT_RESOURCE_CACHE_HPP + + +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty_v +#else +# include +# // sfinae-friendly definition +# define ENTT_IS_EMPTY(Type) (false && std::is_empty_v) +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) __VA_ARGS__ +# endif +#endif + + +#endif + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty_v +#else +# include +# // sfinae-friendly definition +# define ENTT_IS_EMPTY(Type) (false && std::is_empty_v) +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) __VA_ARGS__ +# endif +#endif + + +#endif + + + +namespace entt { + + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + + +} + + +#endif + +// #include "handle.hpp" +#ifndef ENTT_RESOURCE_HANDLE_HPP +#define ENTT_RESOURCE_HANDLE_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_RESOURCE_FWD_HPP +#define ENTT_RESOURCE_FWD_HPP + + +namespace entt { + + +/*! @struct cache */ +template +struct cache; + +/*! @class handle */ +template +class handle; + +/*! @class loader */ +template +class loader; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Shared resource handle. + * + * A shared resource handle is a small class that wraps a resource and keeps it + * alive even if it's deleted from the cache. It can be either copied or + * moved. A handle shares a reference to the same resource with all the other + * handles constructed for the same identifier.
+ * As a rule of thumb, resources should never be copied nor moved. Handles are + * the way to go to keep references to them. + * + * @tparam Resource Type of resource managed by a handle. + */ +template +class handle { + /*! @brief Resource handles are friends of their caches. */ + friend struct cache; + + handle(std::shared_ptr res) ENTT_NOEXCEPT + : resource{std::move(res)} + {} + +public: + /*! @brief Default constructor. */ + handle() ENTT_NOEXCEPT = default; + + /** + * @brief Gets a reference to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource.
+ * An assertion will abort the execution at runtime in debug mode if the + * handle is empty. + * + * @return A reference to the managed resource. + */ + const Resource & get() const ENTT_NOEXCEPT { + ENTT_ASSERT(static_cast(resource)); + return *resource; + } + + /*! @copydoc get */ + Resource & get() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).get()); + } + + /*! @copydoc get */ + operator const Resource & () const ENTT_NOEXCEPT { return get(); } + + /*! @copydoc get */ + operator Resource & () ENTT_NOEXCEPT { return get(); } + + /*! @copydoc get */ + const Resource & operator *() const ENTT_NOEXCEPT { return get(); } + + /*! @copydoc get */ + Resource & operator *() ENTT_NOEXCEPT { return get(); } + + /** + * @brief Gets a pointer to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource.
+ * An assertion will abort the execution at runtime in debug mode if the + * handle is empty. + * + * @return A pointer to the managed resource or `nullptr` if the handle + * contains no resource at all. + */ + const Resource * operator->() const ENTT_NOEXCEPT { + ENTT_ASSERT(static_cast(resource)); + return resource.get(); + } + + /*! @copydoc operator-> */ + Resource * operator->() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).operator->()); + } + + /** + * @brief Returns true if a handle contains a resource, false otherwise. + * @return True if the handle contains a resource, false otherwise. + */ + explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(resource); + } + +private: + std::shared_ptr resource; +}; + + +} + + +#endif + +// #include "loader.hpp" +#ifndef ENTT_RESOURCE_LOADER_HPP +#define ENTT_RESOURCE_LOADER_HPP + + +#include +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Base class for resource loaders. + * + * Resource loaders must inherit from this class and stay true to the CRTP + * idiom. Moreover, a resource loader must expose a public, const member + * function named `load` that accepts a variable number of arguments and returns + * a shared pointer to the resource just created.
+ * As an example: + * + * @code{.cpp} + * struct my_resource {}; + * + * struct my_loader: entt::loader { + * std::shared_ptr load(int) const { + * // use the integer value somehow + * return std::make_shared(); + * } + * }; + * @endcode + * + * In general, resource loaders should not have a state or retain data of any + * type. They should let the cache manage their resources instead. + * + * @note + * Base class and CRTP idiom aren't strictly required with the current + * implementation. One could argue that a cache can easily work with loaders of + * any type. However, future changes won't be breaking ones by forcing the use + * of a base class today and that's why the model is already in its place. + * + * @tparam Loader Type of the derived class. + * @tparam Resource Type of resource for which to use the loader. + */ +template +class loader { + /*! @brief Resource loaders are friends of their caches. */ + friend struct cache; + + /** + * @brief Loads the resource and returns it. + * @tparam Args Types of arguments for the loader. + * @param args Arguments for the loader. + * @return The resource just loaded or an empty pointer in case of errors. + */ + template + std::shared_ptr get(Args &&... args) const { + return static_cast(this)->load(std::forward(args)...); + } +}; + + +} + + +#endif + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @brief Simple cache for resources of a given type. + * + * Minimal implementation of a cache for resources of a given type. It doesn't + * offer much functionalities but it's suitable for small or medium sized + * applications and can be freely inherited to add targeted functionalities for + * large sized applications. + * + * @tparam Resource Type of resources managed by a cache. + */ +template +struct cache { + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of resources managed by a cache. */ + using resource_type = Resource; + + /*! @brief Default constructor. */ + cache() = default; + + /*! @brief Default move constructor. */ + cache(cache &&) = default; + + /*! @brief Default move assignment operator. @return This cache. */ + cache & operator=(cache &&) = default; + + /** + * @brief Number of resources managed by a cache. + * @return Number of resources currently stored. + */ + size_type size() const ENTT_NOEXCEPT { + return resources.size(); + } + + /** + * @brief Returns true if a cache contains no resources, false otherwise. + * @return True if the cache contains no resources, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return resources.empty(); + } + + /** + * @brief Clears a cache and discards all its resources. + * + * Handles are not invalidated and the memory used by a resource isn't + * freed as long as at least a handle keeps the resource itself alive. + */ + void clear() ENTT_NOEXCEPT { + resources.clear(); + } + + /** + * @brief Loads the resource that corresponds to a given identifier. + * + * In case an identifier isn't already present in the cache, it loads its + * resource and stores it aside for future uses. Arguments are forwarded + * directly to the loader in order to construct properly the requested + * resource. + * + * @note + * If the identifier is already present in the cache, this function does + * nothing and the arguments are simply discarded. + * + * @warning + * If the resource cannot be loaded correctly, the returned handle will be + * invalid and any use of it will result in undefined behavior. + * + * @tparam Loader Type of loader to use to load the resource if required. + * @tparam Args Types of arguments to use to load the resource if required. + * @param id Unique resource identifier. + * @param args Arguments to use to load the resource if required. + * @return A handle for the given resource. + */ + template + entt::handle load(const id_type id, Args &&... args) { + static_assert(std::is_base_of_v, Loader>); + entt::handle resource{}; + + if(auto it = resources.find(id); it == resources.cend()) { + if(auto instance = Loader{}.get(std::forward(args)...); instance) { + resources[id] = instance; + resource = std::move(instance); + } + } else { + resource = it->second; + } + + return resource; + } + + /** + * @brief Reloads a resource or loads it for the first time if not present. + * + * Equivalent to the following snippet (pseudocode): + * + * @code{.cpp} + * cache.discard(id); + * cache.load(id, args...); + * @endcode + * + * Arguments are forwarded directly to the loader in order to construct + * properly the requested resource. + * + * @warning + * If the resource cannot be loaded correctly, the returned handle will be + * invalid and any use of it will result in undefined behavior. + * + * @tparam Loader Type of loader to use to load the resource. + * @tparam Args Types of arguments to use to load the resource. + * @param id Unique resource identifier. + * @param args Arguments to use to load the resource. + * @return A handle for the given resource. + */ + template + entt::handle reload(const id_type id, Args &&... args) { + return (discard(id), load(id, std::forward(args)...)); + } + + /** + * @brief Creates a temporary handle for a resource. + * + * Arguments are forwarded directly to the loader in order to construct + * properly the requested resource. The handle isn't stored aside and the + * cache isn't in charge of the lifetime of the resource itself. + * + * @tparam Loader Type of loader to use to load the resource. + * @tparam Args Types of arguments to use to load the resource. + * @param args Arguments to use to load the resource. + * @return A handle for the given resource. + */ + template + entt::handle temp(Args &&... args) const { + return { Loader{}.get(std::forward(args)...) }; + } + + /** + * @brief Creates a handle for a given resource identifier. + * + * A resource handle can be in a either valid or invalid state. In other + * terms, a resource handle is properly initialized with a resource if the + * cache contains the resource itself. Otherwise the returned handle is + * uninitialized and accessing it results in undefined behavior. + * + * @sa handle + * + * @param id Unique resource identifier. + * @return A handle for the given resource. + */ + entt::handle handle(const id_type id) const { + auto it = resources.find(id); + return { it == resources.end() ? nullptr : it->second }; + } + + /** + * @brief Checks if a cache contains a given identifier. + * @param id Unique resource identifier. + * @return True if the cache contains the resource, false otherwise. + */ + bool contains(const id_type id) const { + return (resources.find(id) != resources.cend()); + } + + /** + * @brief Discards the resource that corresponds to a given identifier. + * + * Handles are not invalidated and the memory used by the resource isn't + * freed as long as at least a handle keeps the resource itself alive. + * + * @param id Unique resource identifier. + */ + void discard(const id_type id) { + if(auto it = resources.find(id); it != resources.end()) { + resources.erase(it); + } + } + + /** + * @brief Iterates all resources. + * + * The function object is invoked for each element. It is provided with + * either the resource identifier, the resource handle or both of them.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const id_type); + * void(handle); + * void(const id_type, handle); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + auto begin = resources.begin(); + auto end = resources.end(); + + while(begin != end) { + auto curr = begin++; + + if constexpr(std::is_invocable_v) { + func(curr->first); + } else if constexpr(std::is_invocable_v>) { + func(entt::handle{ curr->second }); + } else { + func(curr->first, entt::handle{ curr->second }); + } + } + } + +private: + std::unordered_map> resources; +}; + + +} + + +#endif + +// #include "resource/handle.hpp" + +// #include "resource/loader.hpp" + +// #include "signal/delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty_v +#else +# include +# // sfinae-friendly definition +# define ENTT_IS_EMPTY(Type) (false && std::is_empty_v) +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) __VA_ARGS__ +# endif +#endif + + +#endif + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +auto function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(*)(Type, Args...), Other &&) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(Class:: *)(Args...), Other &&...) -> Ret(*)(Args...); + + +template +auto function_pointer(Ret(Class:: *)(Args...) const, Other &&...) -> Ret(*)(Args...); + + +template +auto function_pointer(Type Class:: *, Other &&...) -> Type(*)(); + + +template +using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); + + +template +constexpr auto index_sequence_for(Ret(*)(Args...)) { + return std::index_sequence_for{}; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/*! @brief Used to wrap a function or a member of a specified type. */ +template +struct connect_arg_t {}; + + +/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ +template +inline constexpr connect_arg_t connect_arg{}; + + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template +class delegate; + + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class delegate { + using proto_fn_type = Ret(const void *, Args...); + + template + auto wrap(std::index_sequence) ENTT_NOEXCEPT { + return [](const void *, Args... args) -> Ret { + const auto arguments = std::forward_as_tuple(std::forward(args)...); + return Ret(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + }; + } + + template + auto wrap(Type &, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + }; + } + + template + auto wrap(Type *, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + }; + } + +public: + /*! @brief Function type of the delegate. */ + using function_type = Ret(Args...); + + /*! @brief Default constructor. */ + delegate() ENTT_NOEXCEPT + : fn{nullptr}, data{nullptr} + {} + + /** + * @brief Constructs a delegate and connects a free function or an unbound + * member. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + delegate(connect_arg_t) ENTT_NOEXCEPT + : delegate{} + { + connect(); + } + + /** + * @brief Constructs a delegate and connects a free function with payload or + * a bound member. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT + : delegate{} + { + connect(std::forward(value_or_instance)); + } + + /** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + void connect() ENTT_NOEXCEPT { + data = nullptr; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *, Args... args) -> Ret { + return Ret(std::invoke(Candidate, std::forward(args)...)); + }; + } else if constexpr(std::is_member_pointer_v) { + fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); + } else { + fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. + */ + template + void connect(Type &value_or_instance) ENTT_NOEXCEPT { + data = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * @sa connect(Type &) + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + */ + template + void connect(Type *value_or_instance) ENTT_NOEXCEPT { + data = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() ENTT_NOEXCEPT { + fn = nullptr; + data = nullptr; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + const void * instance() const ENTT_NOEXCEPT { + return data; + } + + /** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * delegate has not yet been set. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ + Ret operator()(Args... args) const { + ENTT_ASSERT(fn); + return fn(data, std::forward(args)...); + } + + /** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ + explicit operator bool() const ENTT_NOEXCEPT { + // no need to test also data + return !(fn == nullptr); + } + + /** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ + bool operator==(const delegate &other) const ENTT_NOEXCEPT { + return fn == other.fn && data == other.data; + } + +private: + proto_fn_type *fn; + const void *data; +}; + + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template +bool operator!=(const delegate &lhs, const delegate &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) ENTT_NOEXCEPT +-> delegate>>; + + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template +delegate(connect_arg_t, Type &&) ENTT_NOEXCEPT +-> delegate>>; + + +} + + +#endif + +// #include "signal/dispatcher.hpp" +#ifndef ENTT_SIGNAL_DISPATCHER_HPP +#define ENTT_SIGNAL_DISPATCHER_HPP + + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + + +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + + +#ifndef ENTT_NOEXCEPT +# define ENTT_NOEXCEPT noexcept +#endif + + +#ifndef ENTT_HS_SUFFIX +# define ENTT_HS_SUFFIX _hs +#endif + + +#ifndef ENTT_HWS_SUFFIX +# define ENTT_HWS_SUFFIX _hws +#endif + + +#ifndef ENTT_USE_ATOMIC +# define ENTT_MAYBE_ATOMIC(Type) Type +#else +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#endif + + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + + +#ifndef ENTT_PAGE_SIZE +# define ENTT_PAGE_SIZE 32768 +#endif + + +#ifndef ENTT_ASSERT +# include +# define ENTT_ASSERT(condition) assert(condition) +#endif + + +#ifndef ENTT_NO_ETO +# include +# define ENTT_IS_EMPTY(Type) std::is_empty_v +#else +# include +# // sfinae-friendly definition +# define ENTT_IS_EMPTY(Type) (false && std::is_empty_v) +#endif + + +#ifndef ENTT_STANDARD_CPP +# if defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __clang__ || (defined __GNUC__ && __GNUC__ > 8) +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) constexpr +# elif defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_CONSTEXPR(...) __VA_ARGS__ +# endif +#endif + + +#endif + + + +namespace entt { + + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + + +} + + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + + +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + + +#endif + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +template +struct fnv1a_traits; + + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string { + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *curr) ENTT_NOEXCEPT: str{curr} {} + const Char *str; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + static constexpr id_type helper(const Char *curr) ENTT_NOEXCEPT { + auto value = traits_type::offset; + + while(*curr != 0) { + value = (value ^ static_cast(*(curr++))) * traits_type::prime; + } + + return value; + } + +public: + /*! @brief Character type. */ + using value_type = Char; + /*! @brief Unsigned integer type. */ + using hash_type = id_type; + + /** + * @brief Returns directly the numeric representation of a string. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * const auto value = basic_hashed_string::to_value("my.png"); + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + * @return The numeric representation of the string. + */ + template + static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); + } + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + static hash_type value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { + id_type partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_NOEXCEPT + : str{nullptr}, hash{} + {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * + * Forcing template resolution avoids implicit conversions. An + * human-readable identifier can be anything but a plain, old bunch of + * characters.
+ * Example of use: + * @code{.cpp} + * basic_hashed_string hs{"my.png"}; + * @endcode + * + * @tparam N Number of characters of the identifier. + * @param curr Human-readable identifer. + */ + template + constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT + : str{curr}, hash{helper(curr)} + {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + : str{wrapper.str}, hash{helper(wrapper.str)} + {} + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the instance. + */ + constexpr const value_type * data() const ENTT_NOEXCEPT { + return str; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + constexpr hash_type value() const ENTT_NOEXCEPT { + return hash; + } + + /*! @copydoc data */ + constexpr operator const value_type *() const ENTT_NOEXCEPT { return data(); } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the instance. + */ + constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } + + /** + * @brief Compares two hashed strings. + * @param other Hashed string with which to compare. + * @return True if the two hashed strings are identical, false otherwise. + */ + constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + return hash == other.hash; + } + +private: + const value_type *str; + hash_type hash; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the character type of the hashed string directly from a + * human-readable identifer provided to the constructor. + * + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifer. + */ +template +basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT +-> basic_hashed_string; + + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template +constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + + +} + + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_string{str}; +} + + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { + return entt::hashed_wstring{str}; +} + + +#endif + +// #include "fwd.hpp" + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct ENTT_API type_index { + static id_type next() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/** + * @brief Type index. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + static id_type value() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } +}; + + +/** + * @brief Provides the member constant `value` to true if a given type is + * indexable, false otherwise. + * @tparam Type Potentially indexable type. + */ +template +struct has_type_index: std::false_type {}; + + +/*! @brief has_type_index */ +template +struct has_type_index::value())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially indexable type. + */ +template +inline constexpr bool has_type_index_v = has_type_index::value; + + +/** + * @brief Type info. + * @tparam Type Type for which to generate information. + */ +template +struct ENTT_API type_info { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + static ENTT_PRETTY_FUNCTION_CONSTEXPR() id_type id() ENTT_NOEXCEPT { + ENTT_PRETTY_FUNCTION_CONSTEXPR(static const) auto value = entt::hashed_string::value(ENTT_PRETTY_FUNCTION); + return value; + } +#else + static id_type id() ENTT_NOEXCEPT { + return type_index::value(); + } +#endif +}; + + +} + + +#endif + +// #include "sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "delegate.hpp" + +// #include "fwd.hpp" +#ifndef ENTT_SIGNAL_FWD_HPP +#define ENTT_SIGNAL_FWD_HPP + + +namespace entt { + + +/*! @class delegate */ +template +class delegate; + +/*! @class dispatcher */ +class dispatcher; + +/*! @class emitter */ +template +class emitter; + +/*! @class connection */ +class connection; + +/*! @class scoped_connection */ +struct scoped_connection; + +/*! @class sink */ +template +class sink; + +/*! @class sigh */ +template +class sigh; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Sink class. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Function A valid function type. + */ +template +class sink; + + +/** + * @brief Unmanaged signal handler. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Function A valid function type. + */ +template +class sigh; + + +/** + * @brief Unmanaged signal handler. + * + * It works directly with references to classes and pointers to member functions + * as well as pointers to free functions. Users of this class are in charge of + * disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals to use later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class sigh { + /*! @brief A sink is allowed to modify a signal. */ + friend class sink; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Sink type. */ + using sink_type = entt::sink; + + /** + * @brief Instance type when it comes to connecting member functions. + * @tparam Class Type of class to which the member function belongs. + */ + template + using instance_type = Class *; + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + size_type size() const ENTT_NOEXCEPT { + return calls.size(); + } + + /** + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return calls.empty(); + } + + /** + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. + */ + void publish(Args... args) const { + for(auto &&call: std::as_const(calls)) { + call(args...); + } + } + + /** + * @brief Collects return values from the listeners. + * + * The collector must expose a call operator with the following properties: + * + * * The return type is either `void` or such that it's convertible to + * `bool`. In the second case, a true value will stop the iteration. + * * The list of parameters is empty if `Ret` is `void`, otherwise it + * contains a single element such that `Ret` is convertible to it. + * + * @tparam Func Type of collector to use, if any. + * @param func A valid function object. + * @param args Arguments to use to invoke listeners. + */ + template + void collect(Func func, Args... args) const { + for(auto &&call: calls) { + if constexpr(std::is_void_v) { + if constexpr(std::is_invocable_r_v) { + call(args...); + if(func()) { break; } + } else { + call(args...); + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(call(args...))) { break; } + } else { + func(call(args...)); + } + } + } + } + +private: + std::vector> calls; +}; + + +/** + * @brief Connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it. + */ +class connection { + /*! @brief A sink is allowed to create connection objects. */ + template + friend class sink; + + connection(delegate fn, void *ref) + : disconnect{fn}, signal{ref} + {} + +public: + /*! @brief Default constructor. */ + connection() = default; + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(disconnect); + } + + /*! @brief Breaks the connection. */ + void release() { + if(disconnect) { + disconnect(signal); + disconnect.reset(); + } + } + +private: + delegate disconnect; + void *signal{}; +}; + + +/** + * @brief Scoped connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it.
+ * A scoped connection automatically breaks the link between the two objects + * when it goes out of scope. + */ +struct scoped_connection { + /*! @brief Default constructor. */ + scoped_connection() = default; + + /** + * @brief Constructs a scoped connection from a basic connection. + * @param other A valid connection object. + */ + scoped_connection(const connection &other) + : conn{other} + {} + + /*! @brief Default copy constructor, deleted on purpose. */ + scoped_connection(const scoped_connection &) = delete; + + /*! @brief Automatically breaks the link on destruction. */ + ~scoped_connection() { + conn.release(); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This scoped connection. + */ + scoped_connection & operator=(const scoped_connection &) = delete; + + /** + * @brief Acquires a connection. + * @param other The connection object to acquire. + * @return This scoped connection. + */ + scoped_connection & operator=(connection other) { + conn = std::move(other); + return *this; + } + + /** + * @brief Checks whether a scoped connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(conn); + } + + /*! @brief Breaks the connection. */ + void release() { + conn.release(); + } + +private: + connection conn; +}; + + +/** + * @brief Sink class. + * + * A sink is used to connect listeners to signals and to disconnect them.
+ * The function type for a listener is the one of the signal to which it + * belongs. + * + * The clear separation between a signal and a sink permits to store the former + * as private data member without exposing the publish functionality to the + * users of the class. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class sink { + using signal_type = sigh; + using difference_type = typename std::iterator_traits::difference_type; + + template + static void release(Type value_or_instance, void *signal) { + sink{*static_cast(signal)}.disconnect(value_or_instance); + } + + template + static void release(void *signal) { + sink{*static_cast(signal)}.disconnect(); + } + +public: + /** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ + sink(sigh &ref) ENTT_NOEXCEPT + : offset{}, + signal{&ref} + {} + + /** + * @brief Returns false if at least a listener is connected to the sink. + * @return True if the sink has no listeners connected, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return signal->calls.empty(); + } + + /** + * @brief Returns a sink that connects before a given free function or an + * unbound member. + * @tparam Function A valid free function pointer. + * @return A properly initialized sink object. + */ + template + sink before() { + delegate call{}; + call.template connect(); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = std::distance(it, calls.cend()); + return other; + } + + /** + * @brief Returns a sink that connects before a free function with payload + * or a bound member. + * @tparam Candidate Member or free function to look for. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ + template + sink before(Type &&value_or_instance) { + delegate call{}; + call.template connect(std::forward(value_or_instance)); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = std::distance(it, calls.cend()); + return other; + } + + /** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ + template + sink before(Type &value_or_instance) { + return before(&value_or_instance); + } + + /** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + * @return A properly initialized sink object. + */ + template + sink before(Type *value_or_instance) { + sink other{*this}; + + if(value_or_instance) { + const auto &calls = signal->calls; + const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) { + return delegate.instance() == value_or_instance; + }); + + other.offset = std::distance(it, calls.cend()); + } + + return other; + } + + /** + * @brief Returns a sink that connects before anything else. + * @return A properly initialized sink object. + */ + sink before() { + sink other{*this}; + other.offset = signal->calls.size(); + return other; + } + + /** + * @brief Connects a free function or an unbound member to a signal. + * + * The signal handler performs checks to avoid multiple connections for the + * same function. + * + * @tparam Candidate Function or member to connect to the signal. + * @return A properly initialized connection object. + */ + template + connection connect() { + disconnect(); + + delegate call{}; + call.template connect(); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(); + return { std::move(conn), signal }; + } + + /** + * @brief Connects a free function with payload or a bound member to a + * signal. + * + * The signal isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the signal. On the other side, the signal handler performs + * checks to avoid multiple connections for the same function.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the signal itself. + * + * @tparam Candidate Function or member to connect to the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized connection object. + */ + template + connection connect(Type &&value_or_instance) { + disconnect(value_or_instance); + + delegate call{}; + call.template connect(value_or_instance); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance); + return { std::move(conn), signal }; + } + + /** + * @brief Disconnects a free function or an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + */ + template + void disconnect() { + auto &calls = signal->calls; + delegate call{}; + call.template connect(); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); + } + + /** + * @brief Disconnects a free function with payload or a bound member from a + * signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &&value_or_instance) { + auto &calls = signal->calls; + delegate call{}; + call.template connect(std::forward(value_or_instance)); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &value_or_instance) { + disconnect(&value_or_instance); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type *value_or_instance) { + if(value_or_instance) { + auto &calls = signal->calls; + calls.erase(std::remove_if(calls.begin(), calls.end(), [value_or_instance](const auto &delegate) { + return delegate.instance() == value_or_instance; + }), calls.end()); + } + } + + /*! @brief Disconnects all the listeners from a signal. */ + void disconnect() { + signal->calls.clear(); + } + +private: + difference_type offset; + signal_type *signal; +}; + + +/** + * @brief Deduction guide. + * + * It allows to deduce the function type of a sink directly from the signal it + * refers to. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +sink(sigh &) ENTT_NOEXCEPT -> sink; + + +} + + +#endif + + + +namespace entt { + + +/** + * @brief Basic dispatcher implementation. + * + * A dispatcher can be used either to trigger an immediate event or to enqueue + * events to be published all together once per tick.
+ * Listeners are provided in the form of member functions. For each event of + * type `Event`, listeners are such that they can be invoked with an argument of + * type `const Event &`, no matter what the return type is. + * + * The dispatcher creates instances of the `sigh` class internally. Refer to the + * documentation of the latter for more details. + */ +class dispatcher { + struct basic_pool { + virtual ~basic_pool() = default; + virtual void publish() = 0; + virtual void clear() ENTT_NOEXCEPT = 0; + virtual id_type type_id() const ENTT_NOEXCEPT = 0; + }; + + template + struct pool_handler final: basic_pool { + using signal_type = sigh; + using sink_type = typename signal_type::sink_type; + + void publish() override { + const auto length = events.size(); + + for(std::size_t pos{}; pos < length; ++pos) { + signal.publish(events[pos]); + } + + events.erase(events.cbegin(), events.cbegin()+length); + } + + void clear() ENTT_NOEXCEPT override { + events.clear(); + } + + sink_type sink() ENTT_NOEXCEPT { + return entt::sink{signal}; + } + + template + void trigger(Args &&... args) { + signal.publish(Event{std::forward(args)...}); + } + + template + void enqueue(Args &&... args) { + events.emplace_back(std::forward(args)...); + } + + id_type type_id() const ENTT_NOEXCEPT override { + return type_info::id(); + } + + private: + signal_type signal{}; + std::vector events; + }; + + template + pool_handler & assure() { + static_assert(std::is_same_v>); + + if constexpr(has_type_index_v) { + const auto index = type_index::value(); + + if(!(index < pools.size())) { + pools.resize(index+1); + } + + if(!pools[index]) { + pools[index].reset(new pool_handler{}); + } + + return static_cast &>(*pools[index]); + } else { + auto it = std::find_if(pools.begin(), pools.end(), [id = type_info::id()](const auto &cpool) { return id == cpool->type_id(); }); + return static_cast &>(it == pools.cend() ? *pools.emplace_back(new pool_handler{}) : **it); + } + } + +public: + /** + * @brief Returns a sink object for the given event. + * + * A sink is an opaque object used to connect listeners to events. + * + * The function type for a listener is: + * @code{.cpp} + * void(const Event &); + * @endcode + * + * The order of invocation of the listeners isn't guaranteed. + * + * @sa sink + * + * @tparam Event Type of event of which to get the sink. + * @return A temporary sink object. + */ + template + auto sink() { + return assure().sink(); + } + + /** + * @brief Triggers an immediate event of the given type. + * + * All the listeners registered for the given type are immediately notified. + * The event is discarded after the execution. + * + * @tparam Event Type of event to trigger. + * @tparam Args Types of arguments to use to construct the event. + * @param args Arguments to use to construct the event. + */ + template + void trigger(Args &&... args) { + assure().trigger(std::forward(args)...); + } + + /** + * @brief Triggers an immediate event of the given type. + * + * All the listeners registered for the given type are immediately notified. + * The event is discarded after the execution. + * + * @tparam Event Type of event to trigger. + * @param event An instance of the given type of event. + */ + template + void trigger(Event &&event) { + assure>().trigger(std::forward(event)); + } + + /** + * @brief Enqueues an event of the given type. + * + * An event of the given type is queued. No listener is invoked. Use the + * `update` member function to notify listeners when ready. + * + * @tparam Event Type of event to enqueue. + * @tparam Args Types of arguments to use to construct the event. + * @param args Arguments to use to construct the event. + */ + template + void enqueue(Args &&... args) { + assure().enqueue(std::forward(args)...); + } + + /** + * @brief Enqueues an event of the given type. + * + * An event of the given type is queued. No listener is invoked. Use the + * `update` member function to notify listeners when ready. + * + * @tparam Event Type of event to enqueue. + * @param event An instance of the given type of event. + */ + template + void enqueue(Event &&event) { + assure>().enqueue(std::forward(event)); + } + + /** + * @brief Discards all the events queued so far. + * + * If no types are provided, the dispatcher will clear all the existing + * pools. + * + * @tparam Event Type of events to discard. + */ + template + void clear() { + if constexpr(sizeof...(Event) == 0) { + for(auto &&cpool: pools) { + if(cpool) { + cpool->clear(); + } + } + } else { + (assure().clear(), ...); + } + } + + /** + * @brief Delivers all the pending events of the given type. + * + * This method is blocking and it doesn't return until all the events are + * delivered to the registered listeners. It's responsibility of the users + * to reduce at a minimum the time spent in the bodies of the listeners. + * + * @tparam Event Type of events to send. + */ + template + void update() { + assure().publish(); + } + + /** + * @brief Delivers all the pending events. + * + * This method is blocking and it doesn't return until all the events are + * delivered to the registered listeners. It's responsibility of the users + * to reduce at a minimum the time spent in the bodies of the listeners. + */ + void update() const { + for(auto pos = pools.size(); pos; --pos) { + if(auto &&cpool = pools[pos-1]; cpool) { + cpool->publish(); + } + } + } + +private: + std::vector> pools; +}; + + +} + + +#endif + +// #include "signal/emitter.hpp" +#ifndef ENTT_SIGNAL_EMITTER_HPP +#define ENTT_SIGNAL_EMITTER_HPP + + +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + + + +namespace entt { + + +/** + * @brief General purpose event emitter. + * + * The emitter class template follows the CRTP idiom. To create a custom emitter + * type, derived classes must inherit directly from the base class as: + * + * @code{.cpp} + * struct my_emitter: emitter { + * // ... + * } + * @endcode + * + * Pools for the type of events are created internally on the fly. It's not + * required to specify in advance the full list of accepted types.
+ * Moreover, whenever an event is published, an emitter provides the listeners + * with a reference to itself along with a const reference to the event. + * Therefore listeners have an handy way to work with it without incurring in + * the need of capturing a reference to the emitter. + * + * @tparam Derived Actual type of emitter that extends the class template. + */ +template +class emitter { + struct basic_pool { + virtual ~basic_pool() = default; + virtual bool empty() const ENTT_NOEXCEPT = 0; + virtual void clear() ENTT_NOEXCEPT = 0; + virtual id_type type_id() const ENTT_NOEXCEPT = 0; + }; + + template + struct pool_handler final: basic_pool { + using listener_type = std::function; + using element_type = std::pair; + using container_type = std::list; + using connection_type = typename container_type::iterator; + + bool empty() const ENTT_NOEXCEPT override { + auto pred = [](auto &&element) { return element.first; }; + + return std::all_of(once_list.cbegin(), once_list.cend(), pred) && + std::all_of(on_list.cbegin(), on_list.cend(), pred); + } + + void clear() ENTT_NOEXCEPT override { + if(publishing) { + for(auto &&element: once_list) { + element.first = true; + } + + for(auto &&element: on_list) { + element.first = true; + } + } else { + once_list.clear(); + on_list.clear(); + } + } + + connection_type once(listener_type listener) { + return once_list.emplace(once_list.cend(), false, std::move(listener)); + } + + connection_type on(listener_type listener) { + return on_list.emplace(on_list.cend(), false, std::move(listener)); + } + + void erase(connection_type conn) { + conn->first = true; + + if(!publishing) { + auto pred = [](auto &&element) { return element.first; }; + once_list.remove_if(pred); + on_list.remove_if(pred); + } + } + + void publish(const Event &event, Derived &ref) { + container_type swap_list; + once_list.swap(swap_list); + + publishing = true; + + for(auto &&element: on_list) { + element.first ? void() : element.second(event, ref); + } + + for(auto &&element: swap_list) { + element.first ? void() : element.second(event, ref); + } + + publishing = false; + + on_list.remove_if([](auto &&element) { return element.first; }); + } + + id_type type_id() const ENTT_NOEXCEPT override { + return type_info::id(); + } + + private: + bool publishing{false}; + container_type once_list{}; + container_type on_list{}; + }; + + template + const pool_handler & assure() const { + static_assert(std::is_same_v>); + + if constexpr(has_type_index_v) { + const auto index = type_index::value(); + + if(!(index < pools.size())) { + pools.resize(index+1); + } + + if(!pools[index]) { + pools[index].reset(new pool_handler{}); + } + + return static_cast &>(*pools[index]); + } else { + auto it = std::find_if(pools.begin(), pools.end(), [id = type_info::id()](const auto &cpool) { return id == cpool->type_id(); }); + return static_cast &>(it == pools.cend() ? *pools.emplace_back(new pool_handler{}) : **it); + } + } + + template + pool_handler & assure() { + return const_cast &>(std::as_const(*this).template assure()); + } + +public: + /** @brief Type of listeners accepted for the given event. */ + template + using listener = typename pool_handler::listener_type; + + /** + * @brief Generic connection type for events. + * + * Type of the connection object returned by the event emitter whenever a + * listener for the given type is registered.
+ * It can be used to break connections still in use. + * + * @tparam Event Type of event for which the connection is created. + */ + template + struct connection: private pool_handler::connection_type { + /** @brief Event emitters are friend classes of connections. */ + friend class emitter; + + /*! @brief Default constructor. */ + connection() = default; + + /** + * @brief Creates a connection that wraps its underlying instance. + * @param conn A connection object to wrap. + */ + connection(typename pool_handler::connection_type conn) + : pool_handler::connection_type{std::move(conn)} + {} + }; + + /*! @brief Default constructor. */ + emitter() = default; + + /*! @brief Default destructor. */ + virtual ~emitter() { + static_assert(std::is_base_of_v, Derived>); + } + + /*! @brief Default move constructor. */ + emitter(emitter &&) = default; + + /*! @brief Default move assignment operator. @return This emitter. */ + emitter & operator=(emitter &&) = default; + + /** + * @brief Emits the given event. + * + * All the listeners registered for the specific event type are invoked with + * the given event. The event type must either have a proper constructor for + * the arguments provided or be an aggregate type. + * + * @tparam Event Type of event to publish. + * @tparam Args Types of arguments to use to construct the event. + * @param args Parameters to use to initialize the event. + */ + template + void publish(Args &&... args) { + assure().publish(Event{std::forward(args)...}, *static_cast(this)); + } + + /** + * @brief Registers a long-lived listener with the event emitter. + * + * This method can be used to register a listener designed to be invoked + * more than once for the given event type.
+ * The connection returned by the method can be freely discarded. It's meant + * to be used later to disconnect the listener if required. + * + * The listener is as a callable object that can be moved and the type of + * which is `void(const Event &, Derived &)`. + * + * @note + * Whenever an event is emitted, the emitter provides the listener with a + * reference to the derived class. Listeners don't have to capture those + * instances for later uses. + * + * @tparam Event Type of event to which to connect the listener. + * @param instance The listener to register. + * @return Connection object that can be used to disconnect the listener. + */ + template + connection on(listener instance) { + return assure().on(std::move(instance)); + } + + /** + * @brief Registers a short-lived listener with the event emitter. + * + * This method can be used to register a listener designed to be invoked + * only once for the given event type.
+ * The connection returned by the method can be freely discarded. It's meant + * to be used later to disconnect the listener if required. + * + * The listener is as a callable object that can be moved and the type of + * which is `void(const Event &, Derived &)`. + * + * @note + * Whenever an event is emitted, the emitter provides the listener with a + * reference to the derived class. Listeners don't have to capture those + * instances for later uses. + * + * @tparam Event Type of event to which to connect the listener. + * @param instance The listener to register. + * @return Connection object that can be used to disconnect the listener. + */ + template + connection once(listener instance) { + return assure().once(std::move(instance)); + } + + /** + * @brief Disconnects a listener from the event emitter. + * + * Do not use twice the same connection to disconnect a listener, it results + * in undefined behavior. Once used, discard the connection object. + * + * @tparam Event Type of event of the connection. + * @param conn A valid connection. + */ + template + void erase(connection conn) { + assure().erase(std::move(conn)); + } + + /** + * @brief Disconnects all the listeners for the given event type. + * + * All the connections previously returned for the given event are + * invalidated. Using them results in undefined behavior. + * + * @tparam Event Type of event to reset. + */ + template + void clear() { + assure().clear(); + } + + /** + * @brief Disconnects all the listeners. + * + * All the connections previously returned are invalidated. Using them + * results in undefined behavior. + */ + void clear() ENTT_NOEXCEPT { + for(auto &&cpool: pools) { + if(cpool) { + cpool->clear(); + } + } + } + + /** + * @brief Checks if there are listeners registered for the specific event. + * @tparam Event Type of event to test. + * @return True if there are no listeners registered, false otherwise. + */ + template + bool empty() const { + return assure().empty(); + } + + /** + * @brief Checks if there are listeners registered with the event emitter. + * @return True if there are no listeners registered, false otherwise. + */ + bool empty() const ENTT_NOEXCEPT { + return std::all_of(pools.cbegin(), pools.cend(), [](auto &&cpool) { + return !cpool || cpool->empty(); + }); + } + +private: + mutable std::vector> pools{}; +}; + + +} + + +#endif + +// #include "signal/sigh.hpp" + diff --git a/pyro/external/imgui b/pyro/external/imgui index d6f2cf5..2a5d465 160000 --- a/pyro/external/imgui +++ b/pyro/external/imgui @@ -1 +1 @@ -Subproject commit d6f2cf56ebb98a99611f68ee302e0498e3d008a1 +Subproject commit 2a5d46525a6ac6fd7ee2ece4ad2904cdbe6d2dbf diff --git a/pyro/external/stb_image/premake5.lua b/pyro/external/stb_image/premake5.lua index 7ed48a3..829541b 100644 --- a/pyro/external/stb_image/premake5.lua +++ b/pyro/external/stb_image/premake5.lua @@ -1,31 +1,31 @@ project "stb_image" - kind "StaticLib" - language "C++" + kind "StaticLib" + language "C++" - targetdir ("bin/" .. outputdir .. "/%{prj.name}") - objdir ("inter/" .. outputdir .. "/%{prj.name}") + targetdir ("%{wks.location}/bin/" .. outputdir .. "/%{prj.name}") + objdir ("%{wks.location}/inter/" .. outputdir .. "/%{prj.name}") - files { + files { "stb_image.h", "stb_image.cpp" - } + } - filter "system:windows" + filter "system:windows" systemversion "latest" cppdialect "C++17" staticruntime "On" - filter "configurations:Debug" - defines "PYRO_DEBUG" - runtime "Debug" - symbols "on" + filter "configurations:Debug" + defines "PYRO_DEBUG" + runtime "Debug" + symbols "on" - filter "configurations:Release" - defines "PYRO_RELEASE" - runtime "Release" - optimize "on" + filter "configurations:Release" + defines "PYRO_RELEASE" + runtime "Release" + optimize "on" - filter "configurations:Dist" - defines "PYRO_DIST" - runtime "Release" - optimize "on" \ No newline at end of file + filter "configurations:Dist" + defines "PYRO_DIST" + runtime "Release" + optimize "on" \ No newline at end of file diff --git a/pyro/premake5.lua b/pyro/premake5.lua new file mode 100644 index 0000000..6e38e41 --- /dev/null +++ b/pyro/premake5.lua @@ -0,0 +1,72 @@ + +project "pyro" + +kind "StaticLib" -- Static library (.lib) +language "C++" +cppdialect "C++17" +staticruntime "on" + +targetdir ("%{wks.location}/bin/" .. outputdir .. "/%{prj.name}") +objdir ("%{wks.location}/inter/" .. outputdir .. "/%{prj.name}") + +pchheader "pyro_pch.h" +pchsource "src/pyro_pch.cpp" + +files +{ + -- ** means recursively search down that folder + "src/**.h", + "src/**.cpp", + "external/glm/glm/**.hpp", + "external/glm/glm/**.inl", +} + +defines +{ + "_CRT_SECURE_NO_WARNINGS", + "GLFW_INCLUDE_NONE" +} + +includedirs +{ + "src", + "external/spdlog/include", + "%{IncludeDir.GLFW}", + "%{IncludeDir.Glad}", + "%{IncludeDir.ImGui}", + "%{IncludeDir.glm}", + "%{IncludeDir.stb_image}", + "%{IncludeDir.entt}", +} + +links +{ + "GLFW", + "Glad", + "ImGui", + "opengl32.lib", + "stb_image", +} + +-- filters are used to apply property to some specific configurations only +filter "system:windows" + systemversion "latest" -- windows SDK version + + defines + { + } + +filter "configurations:Debug" + defines "PYRO_DEBUG" + runtime "Debug" + symbols "on" + +filter "configurations:Release" + defines "PYRO_RELEASE" + runtime "Release" + optimize "on" + +filter "configurations:Dist" + defines "PYRO_DIST" + runtime "Release" + optimize "on" \ No newline at end of file diff --git a/pyro/src/platform/opengl/gl_frame_buffer_2d.cpp b/pyro/src/platform/opengl/gl_frame_buffer_2d.cpp index 51daeb8..edcd419 100644 --- a/pyro/src/platform/opengl/gl_frame_buffer_2d.cpp +++ b/pyro/src/platform/opengl/gl_frame_buffer_2d.cpp @@ -1,50 +1,58 @@ -#include "pyro_pch.h" +#include "pyro_pch.h" #include "gl_frame_buffer_2d.h" #include "glad/glad.h" #include "gl_texture.h" - -pyro::gl_frame_buffer_2d::gl_frame_buffer_2d(uint32_t width, uint32_t height) - : m_frame_buffer_id(0) +namespace pyro { + static const uint32_t s_framebuffer_max_size = 8192; +} +pyro::gl_frame_buffer_2d::gl_frame_buffer_2d(framebuffer_props const &properties) + : m_id(0) , m_depth_buffer_id(0) - , m_width(width) - , m_height(height) - , m_clear_color(9.f, 0.f, 0.f, 1.f) + , m_width(properties.width) + , m_height(properties.height) + , m_clear_color(properties.clear_color) { init(); } pyro::gl_frame_buffer_2d::~gl_frame_buffer_2d() { - glDeleteFramebuffers(1, &m_frame_buffer_id); + glDeleteFramebuffers(1, &m_id); } void pyro::gl_frame_buffer_2d::init() { - glGenFramebuffers(1, &m_frame_buffer_id); - glGenRenderbuffers(1, &m_depth_buffer_id); - texture_parameters params; - params.format = e_texture_format::rgba; - params.filter = e_texture_filter::linear; - params.wrap = e_texture_wrap::clamp_to_edge; - - m_texture = make_ref(m_width, m_height, params); - - glBindRenderbuffer(GL_RENDERBUFFER, m_depth_buffer_id); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, m_width, m_height); - - // bind - glBindFramebuffer(GL_FRAMEBUFFER, m_frame_buffer_id); - // set texture buffer - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture->id(), 0); - // set depth buffer - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depth_buffer_id); - // unbind + if(m_id) + { + glDeleteFramebuffers(1, &m_id); + glDeleteTextures(1, &m_color_attachment); + glDeleteTextures(1, &m_depth_attachment); + } + + glCreateFramebuffers(1, &m_id); + glBindFramebuffer(GL_FRAMEBUFFER, m_id); + + glCreateTextures(GL_TEXTURE_2D, 1, &m_color_attachment); + glBindTexture(GL_TEXTURE_2D, m_color_attachment); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_color_attachment, 0); + + glCreateTextures(GL_TEXTURE_2D, 1, &m_depth_attachment); + glBindTexture(GL_TEXTURE_2D, m_depth_attachment); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, m_width, m_height); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_depth_attachment, 0); + + PYRO_CORE_ASSERT(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Framebuffer is incomplete!"); + glBindFramebuffer(GL_FRAMEBUFFER, 0); } void pyro::gl_frame_buffer_2d::bind() const { - glBindFramebuffer(GL_FRAMEBUFFER, m_frame_buffer_id); + glBindFramebuffer(GL_FRAMEBUFFER, m_id); glViewport(0, 0, m_width, m_height); } @@ -53,6 +61,19 @@ void pyro::gl_frame_buffer_2d::unbind() const glBindFramebuffer(GL_FRAMEBUFFER, 0); } +void pyro::gl_frame_buffer_2d::resize(uint32_t width, uint32_t height) +{ + + if(width == 0 || height == 0 || width > s_framebuffer_max_size || height > s_framebuffer_max_size) + { + PYRO_CORE_WARN("Attempted to rezize framebuffer to {0}, {1}", width, height); + return; + } + m_width = width; + m_height = height; + init(); +} + void pyro::gl_frame_buffer_2d::clear_color(glm::vec4 const &color) { m_clear_color = color; @@ -74,8 +95,14 @@ uint32_t pyro::gl_frame_buffer_2d::height() const return m_width; } -pyro::ref -pyro::gl_frame_buffer_2d::texture() const +uint32_t +pyro::gl_frame_buffer_2d::color_attachment() const +{ + return m_color_attachment; +} + +uint32_t +pyro::gl_frame_buffer_2d::depth_attachment() const { - return m_texture; + return m_depth_buffer_id; } diff --git a/pyro/src/platform/opengl/gl_frame_buffer_2d.h b/pyro/src/platform/opengl/gl_frame_buffer_2d.h index 591b305..351d0d3 100644 --- a/pyro/src/platform/opengl/gl_frame_buffer_2d.h +++ b/pyro/src/platform/opengl/gl_frame_buffer_2d.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "pyro/renderer/frame_buffer_2d.h" #include "pyro/renderer/texture.h" @@ -7,31 +7,32 @@ namespace pyro class gl_frame_buffer_2d final : public frame_buffer_2d { public: - gl_frame_buffer_2d(uint32_t width, uint32_t height); + gl_frame_buffer_2d(framebuffer_props const &properties); ~gl_frame_buffer_2d(); void bind() const override; void unbind() const override; + void resize(uint32_t width, uint32_t height) override; void clear_color(glm::vec4 const& color) override; void clear() override; uint32_t width() const override; uint32_t height() const override; - ref texture() const override; + uint32_t color_attachment() const override; + uint32_t depth_attachment() const override; private: void init() override; private: - uint32_t m_frame_buffer_id; + uint32_t m_id; uint32_t m_depth_buffer_id; uint32_t m_width; uint32_t m_height; + uint32_t m_color_attachment; + uint32_t m_depth_attachment; glm::vec4 m_clear_color; - ref m_texture; - - //std::unique_ptr m_depth_texture; }; } diff --git a/pyro/src/pyro.h b/pyro/src/pyro.h index 06aa836..95444c7 100644 --- a/pyro/src/pyro.h +++ b/pyro/src/pyro.h @@ -23,6 +23,12 @@ #include "pyro/renderer/texture.h" #include "pyro/renderer/camera.h" #include "pyro/renderer/camera_controller.h" +#include "pyro/renderer/frame_buffer_2d.h" + +#include "pyro/scene/scene.h" +#include "pyro/scene/entity.h" +#include "pyro/scene/scriptable_entity.h" +#include "pyro/scene/components.h" #include "pyro/imgui/imgui_layer.h" diff --git a/pyro/src/pyro/core/application.cpp b/pyro/src/pyro/core/application.cpp index 5a7be38..06d6689 100644 --- a/pyro/src/pyro/core/application.cpp +++ b/pyro/src/pyro/core/application.cpp @@ -74,15 +74,14 @@ void pyro::application::run() tot_frame_time += m_frame_time; frames++; } - if(layer->is_imgui()) + if(layer->is_imgui() && layer == m_imgui_layer) { - auto const &imgui_layer = std::dynamic_pointer_cast(layer); - PYRO_CORE_ASSERT(imgui_layer, "imgui_layer couldn't be cast!"); - imgui_layer->begin(); + PYRO_CORE_ASSERT(m_imgui_layer, "imgui_layer couldn't be cast!"); + m_imgui_layer->begin(); { - imgui_layer->on_imgui_render(); + m_imgui_layer->on_imgui_render(); } - imgui_layer->end(); + m_imgui_layer->end(); } } tot_time += tot_frame_time; diff --git a/pyro/src/pyro/core/application.h b/pyro/src/pyro/core/application.h index bd53902..6dea835 100644 --- a/pyro/src/pyro/core/application.h +++ b/pyro/src/pyro/core/application.h @@ -32,6 +32,7 @@ namespace pyro /// Returns a reference to the application. static application &instance() { return *s_instance; } + ref gui_layer() { return m_imgui_layer; } static uint32_t fps() { return instance().m_FramesPerSecond; } static uint32_t ups() { return instance().m_UpdatesPerSecond; } @@ -49,11 +50,14 @@ namespace pyro bool on_window_close(window_closed_event &e); bool on_window_resized(window_resize_event &e); + protected: + ref m_imgui_layer = nullptr; + private: std::unique_ptr m_window; layers_stack m_layers_stack; float m_frame_time = 0.f; - pyro::timer *m_timer = nullptr; + timer *m_timer = nullptr; uint32_t m_UpdatesPerSecond; uint32_t m_FramesPerSecond; diff --git a/pyro/src/pyro/core/core.h b/pyro/src/pyro/core/core.h index c7b53bd..9c293c4 100644 --- a/pyro/src/pyro/core/core.h +++ b/pyro/src/pyro/core/core.h @@ -85,7 +85,7 @@ #define PYRO_CORE_ASSERT(expression, ...) #endif -#define BIND_EVENT_FN(fn) std::bind(&fn, this, std::placeholders::_1) +#define BIND_EVENT_FN(fn) [this](auto&&... args) -> decltype(auto) { return this->fn(std::forward(args)...); } #include namespace pyro diff --git a/pyro/src/pyro/debug/profiler.h b/pyro/src/pyro/debug/profiler.h index b0bdc66..5c51400 100644 --- a/pyro/src/pyro/debug/profiler.h +++ b/pyro/src/pyro/debug/profiler.h @@ -41,7 +41,10 @@ namespace debug{ { private: profiler(); + ~profiler(); public: + profiler(const profiler &) = delete; + profiler(profiler &&) = delete; void begin_session(std::string const &name, std::string const &filepath = "result.json"); void end_session(); void write_profile(profile_result const &result); @@ -113,6 +116,12 @@ pyro::debug::profiler::profiler() { } +inline +pyro::debug::profiler::~profiler() +{ + end_session(); +} + inline void pyro::debug::profiler::begin_session(std::string const &name, std::string const &filepath) @@ -290,9 +299,10 @@ pyro::debug::profiler_timer::stop() #define PYRO_PROFILE_BEGIN_SESSION(name, filepath) ::pyro::debug::profiler::get().begin_session(name, filepath) #define PYRO_PROFILE_END_SESSION() ::pyro::debug::profiler::get().end_session() - #define PYRO_PROFILE_SCOPE(name) constexpr auto fixed_name = \ - ::pyro::debug::profiler_utils::cleanup_output_string(name, "__cdecl ");\ - ::pyro::debug::profiler_timer timer##__LINE__(fixed_name.data) + #define PYRO_PROFILE_SCOPE_LINE2(name, line) constexpr auto fixed_name##line = ::pyro::debug::profiler_utils::cleanup_output_string(name, "__cdecl ");\ + ::pyro::debug::profiler_timer timer##line(fixed_name##line.data) + #define PYRO_PROFILE_SCOPE_LINE(name, line) PYRO_PROFILE_SCOPE_LINE2(name, line) + #define PYRO_PROFILE_SCOPE(name) PYRO_PROFILE_SCOPE_LINE(name, __LINE__) #define PYRO_PROFILE_FUNCTION() PYRO_PROFILE_SCOPE(PYRO_FUNC_SIG) #else #define PYRO_PROFILE_BEGIN_SESSION(name, filepath) diff --git a/pyro/src/pyro/imgui/imgui_layer.cpp b/pyro/src/pyro/imgui/imgui_layer.cpp index 39cd720..e4a60ff 100644 --- a/pyro/src/pyro/imgui/imgui_layer.cpp +++ b/pyro/src/pyro/imgui/imgui_layer.cpp @@ -74,9 +74,12 @@ void pyro::imgui_layer::on_imgui_render() void pyro::imgui_layer::on_event(event& e) { - ImGuiIO &io = ImGui::GetIO(); - e.handled |= e.is_in_category(event_category_mouse) && io.WantCaptureMouse; - e.handled |= e.is_in_category(event_category_keyboard) && io.WantCaptureKeyboard; + if(m_block_events) + { + ImGuiIO &io = ImGui::GetIO(); + e.handled |= e.is_in_category(event_category_mouse) & io.WantCaptureMouse; + e.handled |= e.is_in_category(event_category_keyboard) & io.WantCaptureKeyboard; + } } void pyro::imgui_layer::begin() const diff --git a/pyro/src/pyro/imgui/imgui_layer.h b/pyro/src/pyro/imgui/imgui_layer.h index c0e6e8c..98ecb23 100644 --- a/pyro/src/pyro/imgui/imgui_layer.h +++ b/pyro/src/pyro/imgui/imgui_layer.h @@ -18,7 +18,10 @@ namespace pyro void begin() const; void end() const; + void block_events(bool block) { m_block_events = block; } + private: float m_time{0.f}; + bool m_block_events = true; }; } diff --git a/pyro/src/pyro/renderer/camera.cpp b/pyro/src/pyro/renderer/camera.cpp index a1c7898..7c43988 100644 --- a/pyro/src/pyro/renderer/camera.cpp +++ b/pyro/src/pyro/renderer/camera.cpp @@ -37,7 +37,7 @@ void pyro::orthographic_camera::view_matrix(glm::mat4 const& mat) void pyro::orthographic_camera::update_view_matrix() { PYRO_PROFILE_FUNCTION(); - glm::mat4 transform(1); // REMEMBER STR -> ROTATE, TRANSLATE, SCALE in reverse. + glm::mat4 transform(1); // REMEMBER STR -> ROTATE, SCALE, TRANSLATE in reverse. transform = glm::translate(transform, m_position); transform = glm::rotate(transform, glm::radians(m_rotation.z), glm::vec3(0, 0, 1)); diff --git a/pyro/src/pyro/renderer/camera.h b/pyro/src/pyro/renderer/camera.h index 14c5fe7..3344cdb 100644 --- a/pyro/src/pyro/renderer/camera.h +++ b/pyro/src/pyro/renderer/camera.h @@ -1,16 +1,33 @@ #pragma once #include "pyro/core/timestep.h" #define GLM_FORCE_CTOR_INIT -#include "glm/glm.hpp" +#include namespace pyro { - //================= CAMERA INTERFACE ================= + //================= REAL-TIME CAMERA ===================== class PYRO_API camera + { + public: + camera() = default; + camera(const glm::mat4 &projection) + : m_projection(projection) {} + + virtual ~camera() = default; + + const glm::mat4 &projection_matrix() const { return m_projection; } + + protected: + glm::mat4 m_projection{1.f}; + }; + + //================= CAMERA INTERFACE ================= + class PYRO_API camera_base { public: - virtual ~camera() = default; + camera_base() = default; + virtual ~camera_base() = default; virtual void projection_matrix(glm::mat4 const &mat) = 0; virtual glm::mat4 const &projection_matrix() const = 0; @@ -27,7 +44,7 @@ namespace pyro //================= 2D CAMERA ================= - class PYRO_API orthographic_camera final : public camera + class PYRO_API orthographic_camera final : public camera_base { public: orthographic_camera(float left, float right, float bottom, float top); @@ -59,7 +76,7 @@ namespace pyro //================= 3D CAMERA ================= - class PYRO_API perspective_camera : public camera + class PYRO_API perspective_camera : public camera_base { public: enum e_rotation diff --git a/pyro/src/pyro/renderer/camera_controller.cpp b/pyro/src/pyro/renderer/camera_controller.cpp index 5654470..a9a5f79 100644 --- a/pyro/src/pyro/renderer/camera_controller.cpp +++ b/pyro/src/pyro/renderer/camera_controller.cpp @@ -12,7 +12,7 @@ pyro::orthographic_camera_controller::orthographic_camera_controller( float zoom_level /*= 1.f*/, bool rotation /*= false*/ ) - : m_zoom_speed(10.0f) + : m_zoom_speed(0.25f) , m_camera_rotation(0.f) , m_camera_translation_speed(1.0f) , m_camera_rotation_speed(180.0f) @@ -61,7 +61,13 @@ void pyro::orthographic_camera_controller::on_event(event &e) dispatcher.dispatch(BIND_EVENT_FN(orthographic_camera_controller::on_window_resized)); } -pyro::ref +void pyro::orthographic_camera_controller::on_resize(float width, float height) +{ + m_aspect_ratio = width / height; + calculate_view(); +} + +pyro::ref pyro::orthographic_camera_controller::camera() const { return m_camera; @@ -135,8 +141,8 @@ bool pyro::orthographic_camera_controller::on_mouse_scrolled(mouse_scrolled_even bool pyro::orthographic_camera_controller::on_window_resized(window_resize_event &e) { PYRO_PROFILE_FUNCTION(); - m_aspect_ratio = static_cast(e.width()) / static_cast(e.height()); - calculate_view(); + on_resize(static_cast(e.width()), static_cast(e.height())); + // returns if event is handled. return false; } @@ -185,7 +191,12 @@ void pyro::perspective_camera_controller::on_event(event &e) PYRO_PROFILE_FUNCTION(); } -pyro::ref +void pyro::perspective_camera_controller::on_resize(float width, float height) +{ + PYRO_CORE_ASSERT(false, "perspective_camera_controller::on_resize method not implemented.") +} + +pyro::ref pyro::perspective_camera_controller::camera() const { return m_camera; diff --git a/pyro/src/pyro/renderer/camera_controller.h b/pyro/src/pyro/renderer/camera_controller.h index fe9c424..43980b6 100644 --- a/pyro/src/pyro/renderer/camera_controller.h +++ b/pyro/src/pyro/renderer/camera_controller.h @@ -27,8 +27,9 @@ namespace pyro virtual ~camera_controller() = default; virtual void on_update(timestep ts) = 0; virtual void on_event(event &e) = 0; + virtual void on_resize(float width, float height) = 0; - virtual ref camera() const = 0; + virtual ref camera() const = 0; virtual void zoom_level(float level) = 0; virtual float zoom_level() const = 0; @@ -67,7 +68,9 @@ namespace pyro bool rotation = false); void on_update(timestep ts) override; void on_event(event& e) override; - ref camera() const override; + void on_resize(float width, float height) override; + + ref camera() const override; void zoom_level(float level) override; float zoom_level() const override; @@ -84,6 +87,8 @@ namespace pyro private: float m_aspect_ratio; + // Defines the amount of space each mouse-roll moves the camera along + // the front (view) vector or the other right (strafe) vector. float m_zoom_speed; float m_zoom_level; bool m_rotation; @@ -118,7 +123,9 @@ namespace pyro void on_update(timestep ts) override; void on_event(event& e) override; - ref camera() const override; + void on_resize(float width, float height) override; + + ref camera() const override; void zoom_level(float level) override; float zoom_level() const override; void position(glm::vec3 const &pos) override; @@ -141,6 +148,8 @@ namespace pyro /// yaw -> rotation over y axis /// roll -> rotation over z axis glm::vec3 m_camera_rotation; + // Defines the amount of space each mouse-roll moves the camera along + // the front (view) vector or the other right (strafe) vector. const float m_camera_translation_speed; const float m_camera_rotation_speed; diff --git a/pyro/src/pyro/renderer/frame_buffer.h b/pyro/src/pyro/renderer/frame_buffer.h index 5b8e594..0266803 100644 --- a/pyro/src/pyro/renderer/frame_buffer.h +++ b/pyro/src/pyro/renderer/frame_buffer.h @@ -1,8 +1,18 @@ #pragma once #include "pyro/renderer/texture.h" +#include namespace pyro { + struct PYRO_API framebuffer_props + { + uint32_t width = 0; + uint32_t height = 0; + uint32_t samples = 1; + glm::vec4 clear_color = { 9.f, 0.f, 0.f, 1.f }; // { 1.f, 1.f, 1.f, 1.f }; + bool swap_chain_target = false; + }; + class PYRO_API frame_buffer { public: @@ -10,10 +20,12 @@ namespace pyro virtual void bind() const = 0; virtual void unbind() const = 0; virtual void clear() = 0; + virtual void resize(uint32_t width, uint32_t height) = 0; virtual uint32_t width() const = 0; virtual uint32_t height() const = 0; - virtual ref texture() const = 0; + virtual uint32_t color_attachment() const = 0; + virtual uint32_t depth_attachment() const = 0; protected: virtual void init() = 0; }; diff --git a/pyro/src/pyro/renderer/frame_buffer_2d.cpp b/pyro/src/pyro/renderer/frame_buffer_2d.cpp index 44314b1..d0a638a 100644 --- a/pyro/src/pyro/renderer/frame_buffer_2d.cpp +++ b/pyro/src/pyro/renderer/frame_buffer_2d.cpp @@ -4,12 +4,12 @@ #include "platform/opengl/gl_frame_buffer_2d.h" std::shared_ptr -pyro::frame_buffer_2d::create(uint32_t width, uint32_t height) +pyro::frame_buffer_2d::create(framebuffer_props properties) { switch(renderer::api()) { case renderer_api::e_api::none: PYRO_CORE_ASSERT(false, "[frame_buffer_2d] e_renderer_api::none currently not supported!"); return nullptr; - case renderer_api::e_api::opengl: return make_ref(width, height); + case renderer_api::e_api::opengl: return make_ref(properties); } PYRO_CORE_ASSERT(false, "[frame_buffer_2d] Unknown renderer api!"); diff --git a/pyro/src/pyro/renderer/frame_buffer_2d.h b/pyro/src/pyro/renderer/frame_buffer_2d.h index cd6959d..25804c2 100644 --- a/pyro/src/pyro/renderer/frame_buffer_2d.h +++ b/pyro/src/pyro/renderer/frame_buffer_2d.h @@ -9,6 +9,6 @@ namespace pyro virtual void clear_color(glm::vec4 const &color) = 0; public: - static ref create(uint32_t width, uint32_t height); + static ref create(framebuffer_props properties); }; } diff --git a/pyro/src/pyro/renderer/renderer.cpp b/pyro/src/pyro/renderer/renderer.cpp index a4ddf9e..7c8a1c9 100644 --- a/pyro/src/pyro/renderer/renderer.cpp +++ b/pyro/src/pyro/renderer/renderer.cpp @@ -15,7 +15,7 @@ void pyro::renderer::shutdown() renderer_2d::shutdown(); } -void pyro::renderer::begin_scene(camera const &camera, const ref &shader) +void pyro::renderer::begin_scene(camera_base const &camera, const ref &shader) { s_scene_data->view_projection_matrix = camera.view_projection_matrix(); s_scene_data->shader = shader; @@ -23,6 +23,14 @@ void pyro::renderer::begin_scene(camera const &camera, const ref &shader shader->set_mat4("u_view_projection", s_scene_data->view_projection_matrix); } +void pyro::renderer::begin_scene(ref camera, const ref &shader) +{ + s_scene_data->view_projection_matrix = camera->view_projection_matrix(); + s_scene_data->shader = shader; + shader->bind(); + shader->set_mat4("u_view_projection", s_scene_data->view_projection_matrix); +} + void pyro::renderer::on_window_resize(uint32_t width, uint32_t height) { render_command::resize_viewport(0, 0, width, height); diff --git a/pyro/src/pyro/renderer/renderer.h b/pyro/src/pyro/renderer/renderer.h index bc20583..078bc95 100644 --- a/pyro/src/pyro/renderer/renderer.h +++ b/pyro/src/pyro/renderer/renderer.h @@ -3,6 +3,7 @@ #include "render_command.h" #include "shader.h" #include "camera.h" +#include namespace pyro { @@ -12,7 +13,8 @@ namespace pyro static void init(); static void shutdown(); - static void begin_scene(camera const &camera, const ref &shader); + static void begin_scene(camera_base const &camera, const ref &shader); + static void begin_scene(ref camera, const ref &shader); static void end_scene(); static void on_window_resize(uint32_t width, uint32_t height); diff --git a/pyro/src/pyro/renderer/renderer_2d.cpp b/pyro/src/pyro/renderer/renderer_2d.cpp index 4a9fa29..d3a8000 100644 --- a/pyro/src/pyro/renderer/renderer_2d.cpp +++ b/pyro/src/pyro/renderer/renderer_2d.cpp @@ -107,7 +107,7 @@ void pyro::renderer_2d::shutdown() delete[] s_data.quad_vertex_buffer_base; } -void pyro::renderer_2d::begin_scene(ref camera) +void pyro::renderer_2d::begin_scene(ref camera) { PYRO_PROFILE_FUNCTION(); s_data.texture_shader->bind(); @@ -118,6 +118,16 @@ void pyro::renderer_2d::begin_scene(ref camera) reset_render_data(); } +void pyro::renderer_2d::begin_scene(camera const &camera, glm::mat4 const &transform) +{ + glm::mat4 viewProj = camera.projection_matrix() *glm::inverse(transform); + s_data.texture_shader->bind(); + s_data.texture_shader->set_mat4("u_view_projection", viewProj); + s_data.texture_shader->set_int("u_grayscale", false); + + reset_render_data(); +} + void pyro::renderer_2d::end_scene() { PYRO_PROFILE_FUNCTION(); @@ -155,12 +165,33 @@ pyro::ref const &pyro::renderer_2d::current_shader() return s_data.texture_shader; } +void pyro::renderer_2d::draw_quad( + glm::mat4 const &transform, + glm::vec4 color /*= { 1.0f, 1.0f, 1.0f, 1.0f }*/, + ref texture /*= nullptr*/, + float textureIndex /*= 0.f*/, + float tiling_factor /*= 1.f*/) +{ + constexpr glm::vec2 textureCoords[] = { { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f } }; + for(size_t i = 0; i < s_quad_vertex_count; i++) + { + s_data.quad_vertex_buffer_ptr->position = transform * s_data.quad_vertex_positions[i]; + s_data.quad_vertex_buffer_ptr->color = color; + s_data.quad_vertex_buffer_ptr->tex_coord = textureCoords[i]; + s_data.quad_vertex_buffer_ptr->tex_index = textureIndex; + s_data.quad_vertex_buffer_ptr->tiling_factor = tiling_factor; + s_data.quad_vertex_buffer_ptr++; + } + + s_data.quad_index_count += 6; + + s_data.stats.quad_count++; +} + void pyro::renderer_2d::draw_quad(quad_properties const &props) { PYRO_PROFILE_FUNCTION(); - constexpr glm::vec2 textureCoords[] = { { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f } }; - if(s_data.quad_index_count >= renderer_2d::s_max_indices) flush_and_reset(); @@ -194,19 +225,7 @@ void pyro::renderer_2d::draw_quad(quad_properties const &props) transform = glm::rotate(transform, glm::radians(props.rotation), { 0.0f, 0.0f, 1.0f }); transform = glm::scale(transform, { props.size.x, props.size.y, 1.0f }); - for(size_t i = 0; i < s_quad_vertex_count; i++) - { - s_data.quad_vertex_buffer_ptr->position = transform * s_data.quad_vertex_positions[i]; - s_data.quad_vertex_buffer_ptr->color = props.color; - s_data.quad_vertex_buffer_ptr->tex_coord = textureCoords[i]; - s_data.quad_vertex_buffer_ptr->tex_index = textureIndex; - s_data.quad_vertex_buffer_ptr->tiling_factor = props.tiling_factor; - s_data.quad_vertex_buffer_ptr++; - } - - s_data.quad_index_count += 6; - - s_data.stats.quad_count++; + draw_quad(transform, props.color, props.texture, textureIndex, props.tiling_factor); } void pyro::renderer_2d::reset_render_data() diff --git a/pyro/src/pyro/renderer/renderer_2d.h b/pyro/src/pyro/renderer/renderer_2d.h index 08bc189..e57c7ce 100644 --- a/pyro/src/pyro/renderer/renderer_2d.h +++ b/pyro/src/pyro/renderer/renderer_2d.h @@ -21,13 +21,19 @@ namespace pyro static void init(); static void shutdown(); - static void begin_scene(ref camera); + static void begin_scene(ref camera); + static void begin_scene(camera const & camera, glm::mat4 const &transform); static void end_scene(); static void flush(); static ref const& current_shader(); // primitives + static void draw_quad(glm::mat4 const &transform, + glm::vec4 color = { 1.0f, 1.0f, 1.0f, 1.0f }, + ref texture = nullptr, + float textureIndex = 0.f, + float tiling_factor = 1.f); static void draw_quad(quad_properties const& props); static const uint32_t s_quad_vertex_count = 4; diff --git a/pyro/src/pyro/renderer/shader.h b/pyro/src/pyro/renderer/shader.h index 4d43d26..fad7952 100644 --- a/pyro/src/pyro/renderer/shader.h +++ b/pyro/src/pyro/renderer/shader.h @@ -1,5 +1,5 @@ #pragma once -#include "glm/mat4x4.hpp" +#include namespace pyro { diff --git a/pyro/src/pyro/scene/components.h b/pyro/src/pyro/scene/components.h new file mode 100644 index 0000000..670980f --- /dev/null +++ b/pyro/src/pyro/scene/components.h @@ -0,0 +1,86 @@ +#pragma once +#include "pyro/scene/scene_camera.h" +#include "pyro/scene/scriptable_entity.h" +#include +#include + +namespace pyro +{ + struct tag_component + { + inline static char *type_name = "tag_component"; + + std::string tag; + + tag_component() = default; + tag_component(const tag_component &) = default; + tag_component(const std::string &t) + : tag(t) + { + } + }; + + struct transform_component + { + inline static char *type_name = "transform_component"; + glm::mat4 transform{ 1.0f }; + + transform_component() = default; + transform_component(const transform_component &) = default; + transform_component(const glm::mat4 &t) + : transform(t) + { + } + + operator glm::mat4 &() { return transform; } + operator const glm::mat4 &() const { return transform; } + }; + + struct sprite_renderer_component + { + inline static char *type_name = "sprite_renderer_component"; + glm::vec4 color{ 1.0f, 1.0f, 1.0f, 1.0f }; + + sprite_renderer_component() = default; + sprite_renderer_component(const sprite_renderer_component &) = default; + sprite_renderer_component(const glm::vec4 &c) + : color(c) + {} + }; + + + struct camera_component + { + inline static char *type_name = "camera_component"; + + scene_camera camera; + bool primary = true; // TODO: think about moving to Scene + bool fixed_aspect_ratio = false; + + camera_component() = default; + camera_component(const camera_component &) = default; + }; + + struct native_script_component + { + inline static char *type_name = "native_script_component"; + + scriptable_entity *instance = nullptr; + + //[return type] ---- [function ptr name] ----- [parameters] + scriptable_entity* (*instantiate_script_func)(); + void (*destroy_script_func) (native_script_component*); + + template + void bind() + { + instantiate_script_func = []() { return static_cast(new T()); }; + destroy_script_func = [](native_script_component * self) + { + delete self->instance; + self->instance = nullptr; + }; + } + }; + +} \ No newline at end of file diff --git a/pyro/src/pyro/scene/entity.cpp b/pyro/src/pyro/scene/entity.cpp new file mode 100644 index 0000000..31b1220 --- /dev/null +++ b/pyro/src/pyro/scene/entity.cpp @@ -0,0 +1,8 @@ +#include "pyro_pch.h" +#include "entity.h" + + +pyro::entity::entity(entt::entity handle, pyro::scene *scene) + : m_entity_handle(handle), m_scene(scene) +{ +} diff --git a/pyro/src/pyro/scene/entity.h b/pyro/src/pyro/scene/entity.h new file mode 100644 index 0000000..7386eea --- /dev/null +++ b/pyro/src/pyro/scene/entity.h @@ -0,0 +1,72 @@ +#pragma once + +#include "Scene.h" +#include "entt.hpp" + +namespace pyro +{ + + class entity + { + public: + entity() = default; + entity(entt::entity handle, scene *scene); + entity(const entity &other) = default; + + template + T &add_component(Args &&... args) + { + T obj; + std::string message("entity::add_component - entity already has a "); + message += obj.type_name; + message += " component!"; + PYRO_CORE_ASSERT(!has_component(), message); + return m_scene->m_registry.emplace(m_entity_handle, std::forward(args)...); + } + + template + T &get_component() + { + T obj; + std::string message("entity::get_component - entity does not have a "); + message += obj.type_name; + message += " component!"; + PYRO_CORE_ASSERT(has_component(), message); + return m_scene->m_registry.get(m_entity_handle); + } + + template + bool has_component() + { + return m_scene->m_registry.has(m_entity_handle); + } + + template + void remove_component() + { + T obj; + std::string message("entity::remove_component - entity does not have a "); + message += obj.type_name; + message += " component!"; + PYRO_CORE_ASSERT(has_component(), message); + m_scene->m_registry.remove(m_entity_handle); + } + + operator bool() const { return m_entity_handle != entt::null; } + operator uint32_t() const { return static_cast(m_entity_handle); } + + bool operator==(const entity &other) const + { + return m_entity_handle == other.m_entity_handle && m_scene == other.m_scene; + } + + bool operator!=(const entity &other) const + { + return !(*this == other); + } + + private: + entt::entity m_entity_handle{ entt::null }; + scene *m_scene = nullptr; + }; +} \ No newline at end of file diff --git a/pyro/src/pyro/scene/scene.cpp b/pyro/src/pyro/scene/scene.cpp new file mode 100644 index 0000000..a149c6b --- /dev/null +++ b/pyro/src/pyro/scene/scene.cpp @@ -0,0 +1,89 @@ +#include "pyro_pch.h" +#include "scene.h" +#include "entity.h" + +#include "components.h" +#include "pyro/renderer/renderer_2d.h" + + +pyro::scene::scene() {} +pyro::scene::~scene() {} + +pyro::entity pyro::scene::create_entity(std::string const &name /*= ""*/) +{ + entity entity = { m_registry.create(), this }; + entity.add_component(); + auto &tag = entity.add_component(); + tag.tag = name.empty() ? "entity" : name; + return entity; +} + +void pyro::scene::on_update(pyro::timestep const &ts) +{ + // Update scripts + { + m_registry.view().each([=](auto entity, native_script_component &nsc) + { + // TODO:: move on scene play + if(!nsc.instance) + { + nsc.instance = nsc.instantiate_script_func(); + nsc.instance->m_entity = pyro::entity{ entity, this }; + nsc.instance->on_create(); + } + + nsc.instance->on_update(ts); + }); + } +} + +void pyro::scene::on_render() +{ + // Render sprites + camera *main_camera = nullptr; + glm::mat4 *camera_transform = nullptr; + { + auto group = m_registry.view(); + for(auto entity : group) + { + auto [transformComp, cameraComp] = group.get(entity); + + if(cameraComp.primary) + { + main_camera = &cameraComp.camera; + camera_transform = &transformComp.transform; + break; + } + } + } + + if(main_camera) + { + renderer_2d::begin_scene(main_camera->projection_matrix(), *camera_transform); + + auto group = m_registry.group(entt::get); + for(auto entity : group) + { + auto [transform, sprite] = group.get(entity); + + renderer_2d::draw_quad(transform, sprite.color); + } + + renderer_2d::end_scene(); + } +} + +void pyro::scene::on_viewport_resize(uint32_t width, uint32_t height) +{ + m_viewport_width = width; + m_viewport_height = height; + + // Resize our non-FixedAspectRatio cameras + auto view = m_registry.view(); + for(auto entity : view) + { + auto &cameraComponent = view.get(entity); + if(!cameraComponent.fixed_aspect_ratio) + cameraComponent.camera.viewport_size(width, height); + } +} diff --git a/pyro/src/pyro/scene/scene.h b/pyro/src/pyro/scene/scene.h new file mode 100644 index 0000000..0bb9bf8 --- /dev/null +++ b/pyro/src/pyro/scene/scene.h @@ -0,0 +1,27 @@ +#pragma once +#include +#include "pyro/core/timestep.h" + +namespace pyro +{ + class entity; + + class scene + { + public: + scene(); + ~scene(); + + entity create_entity(std::string const &name = std::string()); + void on_update(timestep const &ts); + void on_render(); + void on_viewport_resize(uint32_t width, uint32_t height); + + private: + entt::registry m_registry; + uint32_t m_viewport_width = 0; + uint32_t m_viewport_height = 0; + friend class entity; + friend class scene_hierarchy_panel; + }; +} \ No newline at end of file diff --git a/pyro/src/pyro/scene/scene_camera.cpp b/pyro/src/pyro/scene/scene_camera.cpp new file mode 100644 index 0000000..13579d8 --- /dev/null +++ b/pyro/src/pyro/scene/scene_camera.cpp @@ -0,0 +1,55 @@ +#include "pyro_pch.h" +#include "scene_camera.h" + +#include + + +pyro::scene_camera::scene_camera() +{ + recalculate_projection(); +} + +void pyro::scene_camera::make_perspective(float vertical_fov, float near_clip, float far_clip) +{ + m_projection_type = e_projection_type::perspective; + m_perspective_vertical_fov = vertical_fov; + m_perspective_near = near_clip; + m_perspective_far = far_clip; + recalculate_projection(); +} + +void pyro::scene_camera::make_orthographic(float size, float near_clip, float far_clip) +{ + m_projection_type = e_projection_type::orthographic; + m_orthographic_size = size; + m_orthographic_near = near_clip; + m_orthographic_far = far_clip; + recalculate_projection(); +} + +void pyro::scene_camera::viewport_size(uint32_t width, uint32_t height) +{ + m_aspect_ratio = static_cast(width) / static_cast(height); + recalculate_projection(); +} + +void pyro::scene_camera::recalculate_projection() +{ + if(m_projection_type == e_projection_type::perspective) + { + m_projection = glm::perspective(m_perspective_vertical_fov, + m_aspect_ratio, m_perspective_near, m_perspective_far); + } + else + { + float ortho_left = -m_orthographic_size * m_aspect_ratio * 0.5f; + float ortho_right = m_orthographic_size * m_aspect_ratio * 0.5f; + float ortho_bottom = -m_orthographic_size * 0.5f; + float ortho_top = m_orthographic_size * 0.5f; + + m_projection = glm::ortho( + ortho_left, ortho_right, + ortho_bottom, ortho_top, + m_orthographic_near, m_orthographic_far); + } +} \ No newline at end of file diff --git a/pyro/src/pyro/scene/scene_camera.h b/pyro/src/pyro/scene/scene_camera.h new file mode 100644 index 0000000..13a89c3 --- /dev/null +++ b/pyro/src/pyro/scene/scene_camera.h @@ -0,0 +1,59 @@ +#pragma once +#include "pyro/renderer/camera.h" + +namespace pyro +{ + // It represents all the real-time cameras present in the game. + class scene_camera : public camera + { + public: + enum class e_projection_type { perspective = 0, orthographic = 1 }; + + public: + scene_camera(); + virtual ~scene_camera() = default; + + void make_orthographic(float size, float nearClip, float farClip); + void make_perspective(float vertical_fov, float near_clip, float far_clip); + + void viewport_size(uint32_t width, uint32_t height); + + float perspective_vertical_fov() const { return m_perspective_vertical_fov; } + void perspective_vertical_fov(float vertical_radians) { m_perspective_vertical_fov = vertical_radians; recalculate_projection(); } + + float perspective_near() const { return m_perspective_near; } + void perspective_near(float near_clip) { m_perspective_near = near_clip; recalculate_projection(); } + + float perspective_far() const { return m_perspective_far; } + void perspective_far(float far_clip) { m_perspective_far = far_clip; recalculate_projection(); } + + + float orthographic_size() const { return m_orthographic_size; } + void orthographic_size(float size) { m_orthographic_size = size; recalculate_projection(); } + + float orthographic_near() const { return m_orthographic_near; } + void orthographic_near(float near_clip) { m_orthographic_near = near_clip; recalculate_projection(); } + + float orthographic_far() const { return m_orthographic_far; } + void orthographic_far(float far_clip) { m_orthographic_far = far_clip; recalculate_projection(); } + + e_projection_type projection_type() const { return m_projection_type; } + void projection_type(e_projection_type const &t) { m_projection_type = t; } + + private: + void recalculate_projection(); + private: + e_projection_type m_projection_type{ e_projection_type::orthographic }; + // Field of view angle stored in radians. + float m_perspective_vertical_fov = glm::radians(45.f); + float m_perspective_near = 0.1f; + float m_perspective_far = 1000.f; + + // Full width of the orthographic camera fov. + float m_orthographic_size = 10.0f; + float m_orthographic_near = -1.0f; + float m_orthographic_far = 1.0f; + + float m_aspect_ratio = 0.0f; + }; +} \ No newline at end of file diff --git a/pyro/src/pyro/scene/scriptable_entity.h b/pyro/src/pyro/scene/scriptable_entity.h new file mode 100644 index 0000000..b8eb3d3 --- /dev/null +++ b/pyro/src/pyro/scene/scriptable_entity.h @@ -0,0 +1,26 @@ +#pragma once +#include "pyro/scene/entity.h" + +namespace pyro +{ + class scriptable_entity + { + public: + virtual ~scriptable_entity() = default; + + template + T &get_component() + { + return m_entity.get_component(); + } + + protected: + virtual void on_create(){} + virtual void on_destroy() {} + virtual void on_update(timestep const &ts){} + + private: + entity m_entity; + friend class scene; + }; +} \ No newline at end of file diff --git a/pyro/src/pyro_pch.h b/pyro/src/pyro_pch.h index 58edb87..85c003d 100644 --- a/pyro/src/pyro_pch.h +++ b/pyro/src/pyro_pch.h @@ -16,9 +16,4 @@ #ifdef PYRO_PLATFORM_WIN #include -#endif - -#include "glm/glm.hpp" -#include "glm/gtc/matrix_transform.hpp" -#include "glm/gtc/type_ptr.hpp" -#include "pyro/utils/glm_extensions.h" +#endif diff --git a/sandbox/assets/shaders/texture_2d.glsl b/sandbox/assets/shaders/texture_2d.glsl new file mode 100644 index 0000000..0a70ceb --- /dev/null +++ b/sandbox/assets/shaders/texture_2d.glsl @@ -0,0 +1,68 @@ +/* +* Simple texture shader. +*/ + +#type vertex +#version 430 + +// layout attributes +layout(location = 0) in vec3 a_position; +layout(location = 1) in vec4 a_color; +layout(location = 2) in vec2 a_tex_coord; +layout(location = 3) in float a_tex_index; +layout(location = 4) in float a_tiling_factor; + +uniform mat4 u_view_projection; + +// vertex shader output +out vec4 v_color; +out vec2 v_tex_coord; +out float v_tex_index; +out float v_tiling_factor; + +void main() +{ + v_color = a_color; + v_tex_coord = a_tex_coord; + v_tex_index = a_tex_index; + v_tiling_factor = a_tiling_factor; + gl_Position = u_view_projection * vec4(a_position, 1.0); +} + +#type fragment +#version 430 + +layout(location = 0) out vec4 o_color; + +in vec4 v_color; +in vec2 v_tex_coord; +in float v_tex_index; +in float v_tiling_factor; + +uniform bool u_grayscale = false; +uniform sampler2D u_textures[32]; + +float vignette(vec2 screen_pos, float radius) +{ + float distance = 1.0f - distance(screen_pos * radius, vec2(0.0f)); + distance = clamp(distance, 0.f, 1.f); + distance = sqrt(distance); + return distance; +} + +void main() +{ + vec4 texture_color; + if(u_grayscale) + { + texture_color.rgb = vec3(texture(u_textures[int(v_tex_index)], v_tex_coord * v_tiling_factor).r); + texture_color.a = 1.f; // setting alpha back to non transparent + } + else + { + texture_color = texture(u_textures[int(v_tex_index)], v_tex_coord * v_tiling_factor); + } + o_color = texture_color * v_color; + + //o_color = vec4(v_tex_index / 10.f,0.f,0.f,1.f); +} diff --git a/sandbox/premake5.lua b/sandbox/premake5.lua new file mode 100644 index 0000000..ae1fee1 --- /dev/null +++ b/sandbox/premake5.lua @@ -0,0 +1,51 @@ + +project "sandbox" + +kind "ConsoleApp" +language "C++" +cppdialect "C++17" +staticruntime "on" + +targetdir ("%{wks.location}/bin/" .. outputdir .. "/%{prj.name}") +objdir ("%{wks.location}/inter/" .. outputdir .. "/%{prj.name}") + +files +{ + "src/**.h", + "src/**.cpp" +} + +includedirs +{ + "src", + "%{wks.location}/pyro/external/spdlog/include", + "%{wks.location}/pyro/src", + "%{wks.location}/pyro/external", + "%{IncludeDir.glm}", + "%{IncludeDir.ImGui}", + "%{IncludeDir.entt}", +} + +links +{ + "pyro" +} + +filter "system:windows" + systemversion "latest" + +filter "configurations:Debug" + defines "PYRO_DEBUG" + runtime "Debug" + symbols "on" + +filter "configurations:Release" + defines "PYRO_RELEASE" + runtime "Release" + optimize "on" + +filter "configurations:Dist" + defines "PYRO_DIST" + runtime "Release" + optimize "on" + optimize "on" diff --git a/sandbox/src/layer_2d.cpp b/sandbox/src/layer_2d.cpp index bea5d87..2c2f3b1 100644 --- a/sandbox/src/layer_2d.cpp +++ b/sandbox/src/layer_2d.cpp @@ -1,9 +1,11 @@ #include "layer_2d.h" #include "imgui/imgui.h" +#include -layer_2d::layer_2d() : imgui_layer("Sandbox2D"), -m_2d_camera_controller(1280.0f / 720.0f, true) +layer_2d::layer_2d(float width, float height) + : imgui_layer("sandbox_2d_layer") + , m_2d_camera_controller(glm::vec3{0.f,0.f,0.f}, width / height, true) { } @@ -16,8 +18,9 @@ void layer_2d::on_attach() PYRO_PROFILE_FUNCTION(); imgui_layer::on_attach(); - pyro::texture::wrap(pyro::e_texture_wrap::repeat); - m_checkerboard_texture = pyro::texture_2d::create_from_file("assets/textures/checkerboard.png"); + pyro::texture_parameters params; + params.wrap = pyro::e_texture_wrap::repeat; + m_checkerboard_texture = pyro::texture_2d::create_from_file("assets/textures/checkerboard.png", params); } void layer_2d::on_detach() @@ -25,13 +28,13 @@ void layer_2d::on_detach() PYRO_PROFILE_FUNCTION(); } -static float rotation = 0.0f; void layer_2d::on_update(const pyro::timestep &ts) { // Update PYRO_PROFILE_FUNCTION(); m_2d_camera_controller.on_update(ts); - rotation += ts * 0.5f; + m_rect_rotation += ts * 25.f; + PYRO_CORE_TRACE("rotation - {}", m_rect_rotation); } void layer_2d::on_imgui_render() @@ -76,7 +79,7 @@ void layer_2d::on_imgui_render() props.color = glm::vec4(1); props.position = { -2.f, 0.f, 0.0f }; props.size = {1.f, 1.f}; - props.rotation = rotation; + props.rotation = m_rect_rotation; props.tiling_factor = 20.f; props.color = { .8f, 1.f, .8f, 1.f }; pyro::renderer_2d::draw_quad(props); diff --git a/sandbox/src/layer_2d.h b/sandbox/src/layer_2d.h new file mode 100644 index 0000000..7ecc97f --- /dev/null +++ b/sandbox/src/layer_2d.h @@ -0,0 +1,37 @@ +#pragma once +#include "pyro.h" + +class layer_2d final : public pyro::imgui_layer +{ +public: + layer_2d(float width, float height); + ~layer_2d() override; + void on_attach() override; + void on_detach() override; + void on_update(const pyro::timestep &ts) override; + void on_imgui_render() override; + void on_event(pyro::event &event) override; +private: + pyro::orthographic_camera_controller m_2d_camera_controller; + + // Temp + pyro::ref m_square_va; + pyro::ref m_rect_va; + pyro::ref m_flat_color_shader; + + pyro::ref m_checkerboard_texture; + + glm::vec4 m_square_color = { 0.2f, 0.3f, 0.8f, 1.0f }; + + glm::vec3 m_rect_color{ .2f, .3f, .6f }; + glm::vec3 m_rect_pos{ 0 }; + const float m_rect_speed{ 1.f }; + float m_rect_rotation{ 0.f }; + + struct profile_result + { + char const *name; + float time; + }; + std::vector m_profile_results; +}; \ No newline at end of file diff --git a/sandbox/src/layer_3d.cpp b/sandbox/src/layer_3d.cpp index add8956..bbecaf0 100644 --- a/sandbox/src/layer_3d.cpp +++ b/sandbox/src/layer_3d.cpp @@ -155,7 +155,7 @@ void layer_3d::on_imgui_render() void layer_3d::on_event(pyro::event &event) { - if(event.event_type() == pyro::event_type_e::key_pressed) + if(event.event_type() == pyro::e_event_type::key_pressed) { auto &e = dynamic_cast(event); if(e.key_code() == pyro::key_codes::KEY_TAB) diff --git a/sandbox/src/sandbox_app.cpp b/sandbox/src/sandbox_app.cpp new file mode 100644 index 0000000..146aef8 --- /dev/null +++ b/sandbox/src/sandbox_app.cpp @@ -0,0 +1,60 @@ +// --------- Entry Point --------------- +#include "pyro/core/entry_point.h" +#include "layer_2d.h" +#include "layer_3d.h" + + +class sandbox_app : public pyro::application +{ +public: + sandbox_app(uint32_t width, uint32_t height) + :application("Sandbox", width, height) + { + } + + ~sandbox_app() = default; + + // Inherited via application + virtual void init() override + { + const float width = static_cast(window().width()); + const float height = static_cast(window().height()); + m_imgui_layer = std::make_shared(width,height); + //push_layer(new layer_3d()); + push_layer(m_imgui_layer); + } + + virtual void deinit() override + { + } + + void on_event(pyro::event &event) override + { + application::on_event(event); + + pyro::event_dispatcher dispatcher(event); + // dispatch event on window X pressed + dispatcher.dispatch(BIND_EVENT_FN(sandbox_app::on_key_pressed)); + } + + bool on_key_pressed(pyro::key_pressed_event &event) + { + if(event.event_type() == pyro::e_event_type::key_pressed) + { + if(event.key_code() == pyro::key_codes::KEY_ESCAPE) + { + exit(); + } + //PYRO_TRACE("{0}", static_cast(e.key_code())); + } + return false; + } + +private: +}; + + +pyro::application *pyro::create_application() +{ + return new sandbox_app(1280, 720); +} \ No newline at end of file diff --git a/scripts/generate_solution_vs2017.bat b/scripts/generate_solution_vs2017.bat index 1d2c378..e1527a8 100644 --- a/scripts/generate_solution_vs2017.bat +++ b/scripts/generate_solution_vs2017.bat @@ -1,6 +1,6 @@ @rem run following lines one folder above -pushd %~dp0..\ -call vendor\premake\premake5.exe vs2017 +pushd %~dp0..\ +call vendor\premake\bin\premake5.exe vs2017 popd IF %ERRORLEVEL% NEQ 0 ( @rem silent mode diff --git a/scripts/generate_solution_vs2019.bat b/scripts/generate_solution_vs2019.bat index 0f4ba33..3a20311 100644 --- a/scripts/generate_solution_vs2019.bat +++ b/scripts/generate_solution_vs2019.bat @@ -1,6 +1,6 @@ @rem run following lines one folder above -pushd %~dp0..\ -call vendor\premake\premake5.exe vs2019 +pushd %~dp0..\ +call vendor\premake\bin\premake5.exe vs2019 popd IF %ERRORLEVEL% NEQ 0 ( @rem silent mode diff --git a/tests/premake5.lua b/tests/premake5.lua new file mode 100644 index 0000000..8079bd4 --- /dev/null +++ b/tests/premake5.lua @@ -0,0 +1,56 @@ + +project "tests" + +kind "ConsoleApp" +language "C++" +cppdialect "C++17" +staticruntime "on" + +targetdir ("%{wks.location}/bin/" .. outputdir .. "/%{prj.name}") +objdir ("%{wks.location}/inter/" .. outputdir .. "/%{prj.name}") + +files +{ + "src/**.h", + "src/**.cpp" +} + +includedirs +{ + "src", + "%{wks.location}/ember/src", + "%{wks.location}/pyro/external/spdlog/include", + "%{wks.location}/pyro/src", + "%{wks.location}/pyro/external", + "%{IncludeDir.glm}", + "%{IncludeDir.ImGui}", +} + +links +{ + "ember", + "pyro", +} + +filter "system:windows" + systemversion "latest" + + defines + { + "NOMINMAX", + } + +filter "configurations:Debug" + defines "PYRO_DEBUG" + runtime "Debug" + symbols "on" + +filter "configurations:Release" + defines "PYRO_RELEASE" + runtime "Release" + optimize "on" + +filter "configurations:Dist" + defines "PYRO_DIST" + runtime "Release" + optimize "on" diff --git a/vendor/premake/LICENSE.txt b/vendor/premake/bin/LICENSE.txt similarity index 100% rename from vendor/premake/LICENSE.txt rename to vendor/premake/bin/LICENSE.txt diff --git a/vendor/premake/premake5.exe b/vendor/premake/bin/premake5.exe similarity index 100% rename from vendor/premake/premake5.exe rename to vendor/premake/bin/premake5.exe diff --git a/vendor/premake/premake5.lua b/vendor/premake/premake5.lua new file mode 100644 index 0000000..de06213 --- /dev/null +++ b/vendor/premake/premake5.lua @@ -0,0 +1,17 @@ +project "premake" + kind "Utility" + + targetdir ("%{wks.location}/bin/" .. outputdir .. "/%{prj.name}") + objdir ("%{wks.location}/inter/" .. outputdir .. "/%{prj.name}") + + files + { + "%{wks.location}/**premake5.lua" + } + + postbuildmessage "Regenerating project files with Premake5!" + + postbuildcommands + { + "%{prj.location}bin/premake5 %{_ACTION} --file=\"%{wks.location}premake5.lua\"" + } \ No newline at end of file diff --git a/vendor/premake/premake_customization/solution_items.lua b/vendor/premake/premake_customization/solution_items.lua new file mode 100644 index 0000000..5d11475 --- /dev/null +++ b/vendor/premake/premake_customization/solution_items.lua @@ -0,0 +1,51 @@ +-- Implement the solution_items command for solution-scope files +require('vstudio') + +premake.api.register { + name = "solution_items", + scope = "workspace", + kind = "list:string", +} + +premake.override(premake.vstudio.sln2005, "projects", function(base, wks) + if wks.solution_items and #wks.solution_items > 0 then + local solution_folder_GUID = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}" -- See https://www.codeproject.com/Reference/720512/List-of-Visual-Studio-Project-Type-GUIDs + premake.push("Project(\"" .. solution_folder_GUID .. "\") = \"Solution Items\", \"Solution Items\", \"{" .. os.uuid("Solution Items:" .. wks.name) .. "}\"") + premake.push("ProjectSection(SolutionItems) = preProject") + + for _, path in ipairs(wks.solution_items) do + premake.w(path .. " = " .. path) + end + + premake.pop("EndProjectSection") + premake.pop("EndProject") + end + base(wks) +end) + + +local vs = premake.vstudio.vc2010 + +local function premakeVersionComment(prj) + premake.w('') +end + +local function vcpkg(prj) + if prj.name == 'tests' then + local triplet = 'x64-windows-static' + printf("Appended '%s' to '%s' project", triplet, prj.name) + premake.w(triplet) + end +end + +premake.override(premake.vstudio.vc2010.elements, "project", function(base, prj) + local calls = base(prj) + table.insertafter(calls, vs.xmlDeclaration, premakeVersionComment) + return calls +end) + +premake.override(premake.vstudio.vc2010.elements, "globals", function(base, prj) + local calls = base(prj) + table.insertafter(calls, vs.globals, vcpkg) + return calls +end) \ No newline at end of file